1#include "storm-config.h"
12#include <boost/optional.hpp>
42std::shared_ptr<storm::logic::Formula const> makeRewardBoundedFormula(
43 std::optional<int64_t> lowerBound, std::optional<int64_t> upperBound,
45 static auto const expressionManager = std::make_shared<storm::expressions::ExpressionManager>();
46 std::optional<storm::logic::TimeBound> lowerTimeBound;
47 std::optional<storm::logic::TimeBound> upperTimeBound;
49 lowerTimeBound.emplace(
false, expressionManager->integer(*lowerBound));
52 upperTimeBound.emplace(
false, expressionManager->integer(*upperBound));
55 auto pathFormula = std::make_shared<storm::logic::BoundedUntilFormula>(std::make_shared<storm::logic::BooleanLiteralFormula>(
true),
56 std::make_shared<storm::logic::AtomicLabelFormula>(
"goal"), lowerTimeBound,
58 return std::make_shared<storm::logic::ProbabilityOperatorFormula>(
64 labeling.addLabel(
"init");
65 labeling.addLabelToState(
"init", 0);
66 labeling.addLabel(
"goal");
67 labeling.addLabelToState(
"goal", 1);
72 std::optional<std::vector<ValueType>> stateRewards{
77std::shared_ptr<DtmcType> buildDtmc() {
80 matrixBuilder.addNextValue(0, 1, half);
81 matrixBuilder.addNextValue(0, 2, half);
86 components.rewardModels.emplace(
"r", makeRewardModel());
87 return std::make_shared<DtmcType>(std::move(components));
90std::shared_ptr<MdpType> buildMdp() {
92 matrixBuilder.newRowGroup(0);
95 matrixBuilder.newRowGroup(2);
97 matrixBuilder.newRowGroup(3);
102 return std::make_shared<MdpType>(std::move(components));
105std::shared_ptr<PomdpType> buildPomdp() {
108 matrixBuilder.newRowGroup(0);
109 matrixBuilder.addNextValue(0, 1, half);
110 matrixBuilder.addNextValue(0, 2, half);
111 matrixBuilder.newRowGroup(1);
113 matrixBuilder.newRowGroup(2);
117 components.rewardModels.emplace(
"r", makeRewardModel());
118 components.observabilityClasses = std::vector<uint32_t>{0, 1, 2};
119 return std::make_shared<PomdpType>(std::move(components),
true);
123 std::shared_ptr<storm::logic::Formula const>
const& formula) {
125 EXPECT_TRUE(result->isExplicitQuantitativeCheckResult());
126 return result->asExplicitQuantitativeCheckResult<
ValueType>()[*model->getInitialStates().begin()];
131TEST(RewardBoundUnfolder, FullyUnfoldedDtmcPreservesRewardBoundedProbability) {
132 auto const dtmc = buildDtmc();
133 auto const formula = makeRewardBoundedFormula(std::nullopt, 1);
134 auto const expectedValue = getInitialValue(dtmc, formula);
139 EXPECT_TRUE(result.formula->asProbabilityOperatorFormula().getSubformula().isUntilFormula());
140 EXPECT_EQ(expectedValue, getInitialValue(result.model, result.formula));
143TEST(RewardBoundUnfolder, FullyUnfoldedMdpPreservesChoicesAndOptimalProbabilities) {
144 auto const mdp = buildMdp();
145 for (
auto const direction : {storm::solver::OptimizationDirection::Maximize, storm::solver::OptimizationDirection::Minimize}) {
146 auto const formula = makeRewardBoundedFormula(std::nullopt, 1, direction);
147 auto const expectedValue = getInitialValue(mdp, formula);
154 EXPECT_EQ(2u, result.model->getTransitionMatrix().getRowGroupSize(*result.model->getInitialStates().begin()));
155 EXPECT_TRUE(result.formula->asProbabilityOperatorFormula().getSubformula().isUntilFormula());
156 EXPECT_EQ(expectedValue, getInitialValue(result.model, result.formula));
160TEST(RewardBoundUnfolder, FullyUnfoldedPomdpPreservesObservationsAndActiveBound) {
161 auto pomdp = buildPomdp();
163 auto const formula = makeRewardBoundedFormula(std::nullopt, 1);
166 auto const unfoldedPomdp = result.model->as<PomdpType>();
168 ASSERT_NE(
nullptr, unfoldedPomdp);
169 ASSERT_EQ(4u, unfoldedPomdp->getNumberOfStates());
170 EXPECT_TRUE(unfoldedPomdp->isCanonic());
171 EXPECT_EQ(pomdp->getNrObservations(), unfoldedPomdp->getNrObservations());
172 EXPECT_EQ(pomdp->getObservation(0), unfoldedPomdp->getObservation(0));
173 EXPECT_EQ(pomdp->getObservation(1), unfoldedPomdp->getObservation(1));
174 EXPECT_EQ(pomdp->getObservation(2), unfoldedPomdp->getObservation(2));
175 EXPECT_EQ(pomdp->getObservation(1), unfoldedPomdp->getObservation(3));
176 EXPECT_TRUE(unfoldedPomdp->getStateLabeling().getStates(
"dim0_active").get(0));
177 EXPECT_TRUE(unfoldedPomdp->getStateLabeling().getStates(
"dim0_active").get(1));
178 EXPECT_TRUE(unfoldedPomdp->getStateLabeling().getStates(
"dim0_active").get(2));
179 EXPECT_FALSE(unfoldedPomdp->getStateLabeling().getStates(
"dim0_active").get(3));
180 EXPECT_TRUE(unfoldedPomdp->getStateLabeling().getStates(
"goal").get(1));
181 EXPECT_TRUE(unfoldedPomdp->getStateLabeling().getStates(
"goal").get(3));
182 EXPECT_TRUE(result.formula->asProbabilityOperatorFormula().getSubformula().isUntilFormula());
185TEST(RewardBoundUnfolder, LevelAbstractionPreservesPomdpSpecificComponents) {
186 auto const pomdp = buildPomdp();
187 auto const formula = makeRewardBoundedFormula(std::nullopt, 1);
193 auto const unfoldedPomdp = result.model->as<PomdpType>();
195 ASSERT_NE(
nullptr, unfoldedPomdp);
196 EXPECT_TRUE(unfoldedPomdp->isCanonic());
197 EXPECT_EQ(pomdp->getNrObservations(), unfoldedPomdp->getNrObservations());
198 EXPECT_TRUE(unfoldedPomdp->hasRewardModel(
"r"));
199 EXPECT_TRUE(unfoldedPomdp->hasRewardModel(
"dim0_levelReward"));
200 EXPECT_TRUE(result.formula->asProbabilityOperatorFormula().getSubformula().isBoundedUntilFormula());
203TEST(RewardBoundUnfolder, RejectsLevelWidthOutsideSignedRange) {
204 auto const pomdp = buildPomdp();
205 auto const formula = makeRewardBoundedFormula(std::nullopt, 1);
207 options.
levelWidths = {
static_cast<uint64_t
>(std::numeric_limits<int64_t>::max()) + 1};
210 storm::exceptions::InvalidArgumentException);
213TEST(RewardBoundUnfolder, AcceptsLargestSignedLevelWidth) {
214 auto const dtmc = buildDtmc();
215 auto const formula = makeRewardBoundedFormula(std::nullopt, 1);
217 options.
levelWidths = {
static_cast<uint64_t
>(std::numeric_limits<int64_t>::max())};
220 EXPECT_EQ(getInitialValue(dtmc, formula), getInitialValue(result.model, result.formula));
223TEST(RewardBoundUnfolder, TrivialLowerBoundCanBeFollowedByRewardAwareObservationTransformation) {
224 auto const pomdp = buildPomdp();
225 auto const formula = makeRewardBoundedFormula(0, std::nullopt);
226 auto const& inputBoundedUntilFormula = formula->asProbabilityOperatorFormula().getSubformula().asBoundedUntilFormula();
227 ASSERT_TRUE(inputBoundedUntilFormula.hasLowerBound());
228 EXPECT_FALSE(inputBoundedUntilFormula.isLowerBoundStrict());
233 auto const unfoldedPomdp = unfoldingResult.model->as<PomdpType>();
234 std::set<std::string> levelRewardModels;
235 unfoldingResult.formula->gatherReferencedRewardModels(levelRewardModels);
237 ASSERT_NE(
nullptr, unfoldedPomdp);
238 EXPECT_TRUE(unfoldingResult.formula->asProbabilityOperatorFormula().getSubformula().isUntilFormula());
239 EXPECT_TRUE(levelRewardModels.empty());
241 auto const rewardAwarePomdp =
244 EXPECT_TRUE(rewardAwarePomdp->isCanonic());
245 EXPECT_EQ(5u, rewardAwarePomdp->getNrObservations());
246 EXPECT_EQ(unfoldedPomdp->getNumberOfStates(), rewardAwarePomdp->getNumberOfStates());
249TEST(RewardBoundUnfolder, LevelAbstractionTurnsNonStrictLowerBoundIntoStrictBound) {
250 auto const dtmc = buildDtmc();
251 auto const formula = makeRewardBoundedFormula(1, std::nullopt);
252 auto const& inputBoundedUntilFormula = formula->asProbabilityOperatorFormula().getSubformula().asBoundedUntilFormula();
253 ASSERT_TRUE(inputBoundedUntilFormula.hasLowerBound());
254 EXPECT_FALSE(inputBoundedUntilFormula.isLowerBoundStrict());
255 auto const expectedValue = getInitialValue(dtmc, formula);
260 auto const& unfoldedBoundedUntilFormula = result.formula->asProbabilityOperatorFormula().getSubformula().asBoundedUntilFormula();
262 ASSERT_TRUE(unfoldedBoundedUntilFormula.hasLowerBound());
263 EXPECT_TRUE(unfoldedBoundedUntilFormula.isLowerBoundStrict());
264 EXPECT_EQ(0, unfoldedBoundedUntilFormula.getLowerBound().evaluateAsInt());
265 EXPECT_FALSE(unfoldedBoundedUntilFormula.hasUpperBound());
266 EXPECT_EQ(
"dim0_levelReward", unfoldedBoundedUntilFormula.getTimeBoundReference().getRewardModelName());
267 EXPECT_EQ(expectedValue, getInitialValue(result.model, result.formula));
270TEST(RewardBoundUnfolder, DropsTriviallySatisfiedLowerBoundForDtmc) {
271 auto const dtmc = buildDtmc();
272 auto const formula = makeRewardBoundedFormula(0, std::nullopt);
273 auto const expectedValue = getInitialValue(dtmc, formula);
277 EXPECT_TRUE(result.formula->asProbabilityOperatorFormula().getSubformula().isUntilFormula());
278 EXPECT_EQ(expectedValue, getInitialValue(result.model, result.formula));
281TEST(RewardBoundUnfolder, RejectsDimensionSpecificStateSubformulas) {
282 auto const pomdp = buildPomdp();
283 auto const expressionManager = std::make_shared<storm::expressions::ExpressionManager>();
284 auto const trueFormula = std::make_shared<storm::logic::BooleanLiteralFormula>(
true);
285 std::vector<std::shared_ptr<storm::logic::Formula const>> leftSubformulas{trueFormula, trueFormula};
286 std::vector<std::shared_ptr<storm::logic::Formula const>> rightSubformulas{std::make_shared<storm::logic::AtomicLabelFormula>(
"goal"),
287 std::make_shared<storm::logic::AtomicLabelFormula>(
"init")};
288 std::vector<std::optional<storm::logic::TimeBound>> lowerBounds(2);
289 std::vector<std::optional<storm::logic::TimeBound>> upperBounds{
storm::logic::TimeBound{
false, expressionManager->integer(1)},
292 auto const pathFormula = std::make_shared<storm::logic::BoundedUntilFormula>(leftSubformulas, rightSubformulas, lowerBounds, upperBounds, references);
293 auto const formula = std::make_shared<storm::logic::ProbabilityOperatorFormula>(
TEST(RewardBoundUnfolder, FullyUnfoldedDtmcPreservesRewardBoundedProbability)
This class represents a discrete-time Markov chain.
This class represents a (discrete-time) Markov decision process.
Base class for all sparse models.
This class represents a partially observable Markov decision process.
This class manages the labeling of the state space with a number of (atomic) labels.
A class that can be used to build a sparse matrix by adding value by value.
storm::modelchecker::CheckTask< storm::logic::Formula, ValueType > createTask(std::shared_ptr< const storm::logic::Formula > const &formula, bool onlyInitialStatesRelevant=false)
std::unique_ptr< storm::modelchecker::CheckResult > verifyWithSparseEngine(storm::Environment const &env, std::shared_ptr< storm::models::sparse::Dtmc< ValueType > > const &dtmc, storm::modelchecker::CheckTask< storm::logic::Formula, ValueType > const &task, bool preferEliminationChecker=false)
SFTBDDChecker::ValueType ValueType
FormulaInformation getFormulaInformation(PomdpType const &pomdp, storm::logic::ProbabilityOperatorFormula const &formula)
std::conditional_t< storm::NumberTraits< ValueType >::HasInfinity, ValueType, ExtendedNumber< ValueType > > ExtendedValueType
The type to use for values that may be infinite.
TargetType convertNumber(SourceType const &number)
#define STORM_SILENT_EXPECT_THROW(statement, expected_exception)