13template<
typename ValueType>
14class BeliefMdpBuilderTest :
public ::testing::Test {
16 using BeliefType = Belief<double>;
25 template<
typename Info>
26 static Info makeInfo(uint64_t beliefs) {
28 info.initialBeliefId = 1;
29 info.nrObservationsInPomdp = beliefs;
30 for (uint64_t i = 0;
i < beliefs; ++
i) {
31 BeliefBuilder<BeliefType> builder;
34 info.discoveredBeliefs.addBelief(builder.
build());
39 static PropertyInformation property(
PropertyInformation::Kind kind = PropertyInformation::Kind::ReachabilityProbability) {
40 return {kind, {}, std::nullopt, storm::OptimizationDirection::Maximize, {}};
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)}};
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);
60 EXPECT_EQ(probability, it->getValue());
67using MatrixValueTypes = ::testing::Types<double, storm::RationalNumber>;
70TYPED_TEST(BeliefMdpBuilderTest, MapsSuccessorsInEveryOrderAndPreservesZeros) {
71 auto info = TestFixture::template makeInfo<typename TestFixture::StandardInfo>(3);
72 info.exploredBeliefs = {{0, 2}, {1, 0}, {2, 1}};
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});
78 info.matrix.endCurrentRow();
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();
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)}});
93 TestFixture::checkRow(matrix, 3, {{1, TestFixture::value(1)}});
94 TestFixture::checkRow(matrix, 4, {{2, TestFixture::value(1)}});
96 EXPECT_EQ((std::unordered_map<uint64_t, BeliefId>{{0, 1}, {1, 2}, {2, 0}}), mapping);
97 EXPECT_TRUE(mdp->getStateLabeling().getStateHasLabel(
"init", 0));
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();
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"}};
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)}});
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));
143 EXPECT_EQ(2, info.matrix.transitions.front().targetBelief);
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();
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();
164 property.rewardBounds = {{
"first", std::nullopt, std::nullopt}, {
"second", std::nullopt, std::nullopt}};
165 property.targetObservations = {2, 3};
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, {});
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));
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();
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}});
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)};
226 property.rewardModelName =
"reward";
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());
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)
TYPED_TEST_SUITE(GraphTestAR, TestingTypes,)