Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
BeliefMdpBuilderTest.cpp
Go to the documentation of this file.
1#include "test/storm_gtest.h"
2
3#include <type_traits>
4
9
10namespace {
11using namespace storm::pomdp::beliefs;
12
13template<typename ValueType>
14class BeliefMdpBuilderTest : public ::testing::Test {
15 public:
16 using BeliefType = Belief<double>;
20
21 static ValueType value(double number) {
23 }
24
25 template<typename Info>
26 static Info makeInfo(uint64_t beliefs) {
27 Info info;
28 info.initialBeliefId = 1;
29 info.nrObservationsInPomdp = beliefs;
30 for (uint64_t i = 0; i < beliefs; ++i) {
31 BeliefBuilder<BeliefType> builder;
32 builder.setObservation(i);
33 builder.addValue(i, 1.0);
34 info.discoveredBeliefs.addBelief(builder.build());
35 }
36 return info;
37 }
38
39 static PropertyInformation property(PropertyInformation::Kind kind = PropertyInformation::Kind::ReachabilityProbability) {
40 return {kind, {}, std::nullopt, storm::OptimizationDirection::Maximize, {}};
41 }
42
43 template<typename Info>
44 static auto build(Info const& info, PropertyInformation const& propertyInfo = property()) {
45 std::function<std::unordered_map<std::string, ValueType>(BeliefType const&)> cutOff = [](BeliefType const&) {
46 return std::unordered_map<std::string, ValueType>{{"cutoff", value(0.75)}};
47 };
48 return buildBeliefMdp(info, propertyInfo, cutOff);
49 }
50
51 static void checkRow(storm::storage::SparseMatrix<ValueType> const& matrix, uint64_t row, std::initializer_list<std::pair<uint64_t, ValueType>> expected) {
52 auto const actual = matrix.getRow(row);
53 ASSERT_EQ(expected.size(), actual.getNumberOfEntries());
54 auto it = actual.begin();
55 for (auto const& [column, probability] : expected) {
56 EXPECT_EQ(column, it->getColumn());
57 if constexpr (std::is_same_v<ValueType, double>) {
58 EXPECT_NEAR(probability, it->getValue(), 1e-14);
59 } else {
60 EXPECT_EQ(probability, it->getValue());
61 }
62 ++it;
63 }
64 }
65};
66
67using MatrixValueTypes = ::testing::Types<double, storm::RationalNumber>;
68TYPED_TEST_SUITE(BeliefMdpBuilderTest, MatrixValueTypes, );
69
70TYPED_TEST(BeliefMdpBuilderTest, MapsSuccessorsInEveryOrderAndPreservesZeros) {
71 auto info = TestFixture::template makeInfo<typename TestFixture::StandardInfo>(3);
72 info.exploredBeliefs = {{0, 2}, {1, 0}, {2, 1}};
73 // These belief IDs map to ascending, descending, and mixed state IDs respectively.
74 for (auto const& order : {std::vector<BeliefId>{1, 2, 0}, std::vector<BeliefId>{0, 2, 1}, std::vector<BeliefId>{2, 0, 1}}) {
75 for (auto id : order) {
76 info.matrix.transitions.push_back({TestFixture::value(id == 2 ? 0.0 : 0.5), id});
77 }
78 info.matrix.endCurrentRow();
79 }
80 info.matrix.endCurrentRowGroup();
81 info.matrix.transitions.push_back({TestFixture::value(1), 2});
82 info.matrix.endCurrentRow();
83 info.matrix.endCurrentRowGroup();
84 info.matrix.transitions.push_back({TestFixture::value(1), 0});
85 info.matrix.endCurrentRow();
86 info.matrix.endCurrentRowGroup();
87
88 auto const [mdp, mapping] = TestFixture::build(info);
89 auto const& matrix = mdp->getTransitionMatrix();
90 for (uint64_t row = 0; row < 3; ++row) {
91 TestFixture::checkRow(matrix, row, {{0, TestFixture::value(0.5)}, {1, TestFixture::value(0)}, {2, TestFixture::value(0.5)}});
92 }
93 TestFixture::checkRow(matrix, 3, {{1, TestFixture::value(1)}});
94 TestFixture::checkRow(matrix, 4, {{2, TestFixture::value(1)}});
95 EXPECT_EQ((std::vector<uint64_t>{0, 3, 4, 5, 6, 7}), matrix.getRowGroupIndices());
96 EXPECT_EQ((std::unordered_map<uint64_t, BeliefId>{{0, 1}, {1, 2}, {2, 0}}), mapping);
97 EXPECT_TRUE(mdp->getStateLabeling().getStateHasLabel("init", 0));
98}
99
100TYPED_TEST(BeliefMdpBuilderTest, MergesDuplicatesAndPreservesFrontierTerminalAndLabels) {
101 auto info = TestFixture::template makeInfo<typename TestFixture::StandardInfo>(4);
102 info.exploredBeliefs = {{0, 1}, {1, 0}};
103 info.terminalBeliefValues = {{3, TestFixture::value(0.5)}};
104 info.matrix.transitions = {{TestFixture::value(0.25), 2},
105 {TestFixture::value(0.125), 0},
106 {TestFixture::value(0.25), 3},
107 {TestFixture::value(0.25), 1},
108 {TestFixture::value(0.125), 0}};
109 info.matrix.endCurrentRow();
110 // No ordinary successors: the scratch buffer from the previous choice must be cleared.
111 info.matrix.transitions.push_back({TestFixture::value(1), 3});
112 info.matrix.endCurrentRow();
113 info.matrix.endCurrentRowGroup();
114 info.matrix.transitions.push_back({TestFixture::value(1), 0});
115 info.matrix.endCurrentRow();
116 info.matrix.endCurrentRowGroup();
117 info.matrix.choiceLabels = {{"mixed"}, {"sinks"}, {"self"}};
118
119 auto const [mdp, mapping] = TestFixture::build(info);
120 auto const& matrix = mdp->getTransitionMatrix();
121 TestFixture::checkRow(matrix, 0,
122 {{0, TestFixture::value(0.25)},
123 {1, TestFixture::value(0.25)},
124 {2, TestFixture::value(0.25)},
125 {3, TestFixture::value(0.125)},
126 {4, TestFixture::value(0.125)}});
127 TestFixture::checkRow(matrix, 1, {{3, TestFixture::value(0.5)}, {4, TestFixture::value(0.5)}});
128 TestFixture::checkRow(matrix, 2, {{1, TestFixture::value(1)}});
129 TestFixture::checkRow(matrix, 3, {{3, TestFixture::value(0.75)}, {4, TestFixture::value(0.25)}});
130 TestFixture::checkRow(matrix, 4, {{3, TestFixture::value(1)}});
131 TestFixture::checkRow(matrix, 5, {{4, TestFixture::value(1)}});
132 EXPECT_EQ((std::vector<uint64_t>{0, 2, 3, 4, 5, 6}), matrix.getRowGroupIndices());
133 EXPECT_EQ((std::unordered_map<uint64_t, BeliefId>{{0, 1}, {1, 0}, {2, 2}}), mapping);
134 EXPECT_TRUE(mdp->getStateLabeling().getStateHasLabel("truncated", 2));
135 EXPECT_TRUE(mdp->getStateLabeling().getStateHasLabel("target", 3));
136 EXPECT_TRUE(mdp->getStateLabeling().getStateHasLabel("bottom", 4));
137 ASSERT_TRUE(mdp->hasChoiceLabeling());
138 for (auto const& [choice, label] :
139 std::vector<std::pair<uint64_t, std::string>>{{0, "mixed"}, {1, "sinks"}, {2, "self"}, {3, "cutoff"}, {4, "__loop__"}, {5, "__loop__"}}) {
140 EXPECT_EQ((std::set<std::string>{label}), mdp->getChoiceLabeling().getLabelsOfChoice(choice));
141 }
142 // Construction must not reorder the exploration information itself.
143 EXPECT_EQ(2, info.matrix.transitions.front().targetBelief);
144}
145
146TYPED_TEST(BeliefMdpBuilderTest, KeepsMultipleRewardDimensionsAttachedToSuccessors) {
147 auto info = TestFixture::template makeInfo<typename TestFixture::RewardInfo>(4);
148 info.exploredBeliefs = {{0, 1}, {1, 0}, {2, 2}};
149 auto const n = TestFixture::value;
150 info.matrix.transitions = {{n(0.25), 3, {n(5), n(0)}}, {n(0.25), 0, {n(0), n(3)}}, {n(0.25), 2, {n(2), n(7)}}, {n(0.25), 1, {n(11), n(0)}}};
151 info.matrix.endCurrentRow();
152 // Duplicate successors are valid when all their transition rewards are zero.
153 info.matrix.transitions.push_back({n(0.5), 2, {n(0), n(0)}});
154 info.matrix.transitions.push_back({n(0.25), 1, {n(0), n(0)}});
155 info.matrix.transitions.push_back({n(0.25), 2, {n(0), n(0)}});
156 info.matrix.endCurrentRow();
157 info.matrix.endCurrentRowGroup();
158 for (auto id : {0ul, 2ul}) {
159 info.matrix.transitions.push_back({n(1), id, {n(0), n(0)}});
160 info.matrix.endCurrentRow();
161 info.matrix.endCurrentRowGroup();
162 }
163 auto property = TestFixture::property(PropertyInformation::Kind::RewardBoundedReachabilityProbability);
164 property.rewardBounds = {{"first", std::nullopt, std::nullopt}, {"second", std::nullopt, std::nullopt}};
165 property.targetObservations = {2, 3};
166
167 auto const [mdp, mapping] = TestFixture::build(info, property);
168 auto const& probabilities = mdp->getTransitionMatrix();
169 auto const& first = mdp->getRewardModel("first").getTransitionRewardMatrix();
170 auto const& second = mdp->getRewardModel("second").getTransitionRewardMatrix();
171 TestFixture::checkRow(probabilities, 0, {{0, n(0.25)}, {1, n(0.25)}, {2, n(0.25)}, {3, n(0.25)}});
172 TestFixture::checkRow(probabilities, 1, {{0, n(0.25)}, {2, n(0.75)}});
173 TestFixture::checkRow(first, 0, {{0, n(11)}, {2, n(2)}, {3, n(5)}});
174 TestFixture::checkRow(second, 0, {{1, n(3)}, {2, n(7)}});
175 for (uint64_t row = 1; row < probabilities.getRowCount(); ++row) {
176 TestFixture::checkRow(first, row, {});
177 TestFixture::checkRow(second, row, {});
178 }
179 EXPECT_EQ(probabilities.getRowGroupIndices(), first.getRowGroupIndices());
180 EXPECT_EQ(probabilities.getRowGroupIndices(), second.getRowGroupIndices());
181 EXPECT_EQ((std::unordered_map<uint64_t, BeliefId>{{0, 1}, {1, 0}, {2, 2}, {3, 3}}), mapping);
182 EXPECT_TRUE(mdp->getStateLabeling().getStateHasLabel("target", 2));
183 EXPECT_TRUE(mdp->getStateLabeling().getStateHasLabel("target", 3));
184}
185
186TYPED_TEST(BeliefMdpBuilderTest, AccumulatesClippingProbabilitiesForBothDirections) {
187 auto info = TestFixture::template makeInfo<typename TestFixture::ClippingInfo>(4);
188 info.exploredBeliefs = {{0, 1}, {1, 0}};
189 auto const n = TestFixture::value;
190 info.terminalBeliefValues = {{3, n(0.5)}};
191 info.matrix.transitions = {{n(0.125), 2, {n(0.125), std::nullopt}},
192 {n(0.125), 0, {n(0.125), std::nullopt}},
193 {n(0.25), 3, {n(0.125), std::nullopt}},
194 {n(0.125), 1, {std::nullopt, std::nullopt}}};
195 info.matrix.endCurrentRow();
196 info.matrix.endCurrentRowGroup();
197 info.matrix.transitions.push_back({n(1), 0, {std::nullopt, std::nullopt}});
198 info.matrix.endCurrentRow();
199 info.matrix.endCurrentRowGroup();
200
201 for (auto direction : {storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Maximize}) {
202 auto property = TestFixture::property();
203 property.dir = direction;
204 auto const [mdp, mapping] = TestFixture::build(info, property);
205 auto const target = direction == storm::OptimizationDirection::Minimize ? n(0.5) : n(0.125);
206 auto const bottom = direction == storm::OptimizationDirection::Minimize ? n(0.125) : n(0.5);
207 TestFixture::checkRow(mdp->getTransitionMatrix(), 0, {{0, n(0.125)}, {1, n(0.125)}, {2, n(0.125)}, {3, target}, {4, bottom}});
208 }
209}
210
211TYPED_TEST(BeliefMdpBuilderTest, AccumulatesTerminalRewardsAndClippingPenalties) {
212 auto info = TestFixture::template makeInfo<typename TestFixture::ClippingInfo>(4);
213 info.exploredBeliefs = {{0, 1}, {1, 0}};
214 auto const n = TestFixture::value;
215 info.terminalBeliefValues = {{3, n(4)}};
216 info.matrix.transitions = {{n(0.25), 2, {n(0.125), n(2)}}, {n(0.25), 0, {n(0.125), std::nullopt}}, {n(0.125), 3, {n(0.125), n(3)}}};
217 info.matrix.endCurrentRow();
218 info.matrix.transitions.push_back({n(1), 3, {std::nullopt, std::nullopt}});
219 info.matrix.endCurrentRow();
220 info.matrix.endCurrentRowGroup();
221 info.matrix.transitions.push_back({n(1), 0, {std::nullopt, std::nullopt}});
222 info.matrix.endCurrentRow();
223 info.matrix.endCurrentRowGroup();
224 info.actionRewards = {n(1), n(2), n(3)};
225 auto property = TestFixture::property(PropertyInformation::Kind::ExpectedTotalReachabilityReward);
226 property.rewardModelName = "reward";
227
228 auto const [mdp, mapping] = TestFixture::build(info, property);
229 TestFixture::checkRow(mdp->getTransitionMatrix(), 0, {{1, n(0.25)}, {2, n(0.25)}, {3, n(0.5)}});
230 TestFixture::checkRow(mdp->getTransitionMatrix(), 1, {{3, n(1)}});
231 EXPECT_EQ((std::vector<TypeParam>{n(6.5), n(6), n(3), n(0.75), n(0), n(0)}), mdp->getRewardModel("reward").getStateActionRewardVector());
232}
233} // namespace
BeliefType build()
Builds the belief and returns it.
void setObservation(BeliefObservationType const &observation)
Sets the observation of the belief.
void addValue(BeliefStateType const &state, ValueType const &value)
Adds a probability value to the given state.
const_rows getRow(index_type row) const
Returns an object representing the given row.
std::vector< index_type > const & getRowGroupIndices() const
Returns the grouping of rows of this matrix.
SFTBDDChecker::ValueType ValueType
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.
std::pair< std::shared_ptr< models::sparse::Mdp< BeliefMdpValueType > >, std::unordered_map< uint64_t, BeliefId > > buildBeliefMdp(ExplorationInformation< BeliefMdpValueType, BeliefType, ExtraTransitionData... > const &explorationInformation, PropertyInformation const &propertyInformation, std::function< std::unordered_map< std::string, BeliefMdpValueType >(BeliefType const &)> const &computeCutOffValueMap)
Builds a belief MDP from the given exploration information and property information.
ExplorationInformation< BeliefMdpValueType, BeliefType, std::vector< BeliefMdpValueType > > RewardAwareExplorationInformation
Exploration information that stores a reward vector on each transition.
TargetType convertNumber(SourceType const &number)
TYPED_TEST(GraphTestAR, SymbolicProb01StochasticGameDieSmall)
Definition GraphTest.cpp:64
TYPED_TEST_SUITE(GraphTestAR, TestingTypes,)