Storm 1.13.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
GenerateMonitorVerifierTest.cpp
Go to the documentation of this file.
1#include "storm-config.h"
2#include "test/storm_gtest.h"
3
5
14
15namespace {
16
17// Build a 3-state DTMC used as the system model (MC):
18// s0 (init): 0.6 -> s1, 0.4 -> s2
19// s1 ("a", "good"): self-loop
20// s2 ("b"): self-loop
22 storm::storage::SparseMatrixBuilder<double> builder(3, 3, 4, false, false);
23 builder.addNextValue(0, 1, 0.6);
24 builder.addNextValue(0, 2, 0.4);
25 builder.addNextValue(1, 1, 1.0);
26 builder.addNextValue(2, 2, 1.0);
27 auto matrix = builder.build();
28
30 labeling.addLabel("init");
31 labeling.addLabelToState("init", 0);
32 labeling.addLabel("a");
33 labeling.addLabelToState("a", 1);
34 labeling.addLabel("b");
35 labeling.addLabelToState("b", 2);
36 labeling.addLabel("good");
37 labeling.addLabelToState("good", 1);
38
39 storm::storage::sparse::ModelComponents<double> components(std::move(matrix), std::move(labeling));
40 return storm::models::sparse::Dtmc<double>(std::move(components));
41}
42
43// Build a 3-state MDP used as the 2-step monitor:
44// m0 ("step0", init): [a] -> m1, [b] -> m1
45// m1 ("step1", "accepting"): [a] -> m2, [b] -> m2
46// m2 ("step2", "horizon"): [a] -> m2 (self-loop; horizon overrides in product)
47//
48// The monitor accepts at step1 and then triggers a horizon reset at step2.
49storm::models::sparse::Mdp<double> buildSimpleMonitor() {
50 // 5 total choice rows across 3 row groups (states).
51 storm::storage::SparseMatrixBuilder<double> builder(0, 0, 0, false, true);
52 builder.newRowGroup(0); // state 0: rows 0-1
53 builder.addNextValue(0, 1, 1.0); // [a] -> m1
54 builder.addNextValue(1, 1, 1.0); // [b] -> m1
55 builder.newRowGroup(2); // state 1: rows 2-3
56 builder.addNextValue(2, 2, 1.0); // [a] -> m2
57 builder.addNextValue(3, 2, 1.0); // [b] -> m2
58 builder.newRowGroup(4); // state 2: row 4
59 builder.addNextValue(4, 2, 1.0); // [a] -> m2 (self-loop)
60 auto matrix = builder.build();
61
63 labeling.addLabel("init");
64 labeling.addLabelToState("init", 0);
65 labeling.addLabel("step0");
66 labeling.addLabelToState("step0", 0);
67 labeling.addLabel("step1");
68 labeling.addLabelToState("step1", 1);
69 labeling.addLabel("accepting");
70 labeling.addLabelToState("accepting", 1);
71 labeling.addLabel("step2");
72 labeling.addLabelToState("step2", 2);
73 labeling.addLabel("horizon");
74 labeling.addLabelToState("horizon", 2);
75
76 // Choice labeling: 5 rows total.
77 // "a": rows 0, 2, 4 ; "b": rows 1, 3
79 storm::storage::BitVector aChoices(5);
80 aChoices.set(0);
81 aChoices.set(2);
82 aChoices.set(4);
83 choiceLabeling.addLabel("a", aChoices);
84 storm::storage::BitVector bChoices(5);
85 bChoices.set(1);
86 bChoices.set(3);
87 choiceLabeling.addLabel("b", bChoices);
88
89 storm::storage::sparse::ModelComponents<double> components(std::move(matrix), std::move(labeling));
90 components.choiceLabeling = std::move(choiceLabeling);
91 return storm::models::sparse::Mdp<double>(std::move(components));
92}
93
94// Helper: create the generator with common settings and produce the product.
95std::shared_ptr<storm::generator::MonitorVerifier<double>> makeProduct(storm::models::sparse::Dtmc<double> const& mc,
98 std::vector<double> risk = {0.0, 1.0, 0.0}) {
99 auto exprManager = std::make_shared<storm::expressions::ExpressionManager>();
100 storm::generator::GenerateMonitorVerifier<double> gen(mc, monitor, exprManager, options);
101 gen.setRisk(risk);
102 return gen.createProduct();
103}
104
105TEST(MonitorVerifier, ProductHasExpectedLabels) {
106 auto mc = buildSimpleMC();
107 auto monitor = buildSimpleMonitor();
108
110 options.useRestartSemantics = false;
111
112 auto mv = makeProduct(mc, monitor, options);
113 const auto& product = mv->getProduct();
114 const auto& labeling = product.getStateLabeling();
115
116 EXPECT_TRUE(labeling.containsLabel("init"));
117 EXPECT_TRUE(labeling.containsLabel("goal"));
118 EXPECT_TRUE(labeling.containsLabel("stop"));
119 EXPECT_TRUE(labeling.containsLabel("condition"));
120 EXPECT_TRUE(labeling.containsLabel("sink"));
121
122 EXPECT_EQ(1ul, labeling.getStates("goal").getNumberOfSetBits());
123 EXPECT_EQ(1ul, labeling.getStates("stop").getNumberOfSetBits());
124 EXPECT_EQ(1ul, labeling.getStates("sink").getNumberOfSetBits());
125 EXPECT_GE(labeling.getStates("init").getNumberOfSetBits(), 1ul);
126
127 // "condition" must be exactly the union of "goal" and "stop".
128 EXPECT_EQ(2ul, labeling.getStates("condition").getNumberOfSetBits());
129 EXPECT_EQ(labeling.getStates("goal") | labeling.getStates("stop"), labeling.getStates("condition"));
130
131 // The product POMDP must carry choice labeling.
132 EXPECT_TRUE(product.hasChoiceLabeling());
133}
134
135TEST(MonitorVerifier, ObservationMapEntries) {
136 auto mc = buildSimpleMC();
137 auto monitor = buildSimpleMonitor();
138
140 options.useRestartSemantics = false;
141
142 auto mv = makeProduct(mc, monitor, options);
143 const auto& obsMap = mv->getObservationMap();
144
145 // One entry per distinct (step, accepting) combination in the monitor.
146 EXPECT_TRUE(obsMap.contains(std::make_pair(0u, false))); // m0: step0, not accepting
147 EXPECT_TRUE(obsMap.contains(std::make_pair(1u, true))); // m1: step1, accepting
148 EXPECT_TRUE(obsMap.contains(std::make_pair(2u, false))); // m2: step2, not accepting (horizon)
149
150 // All mapped observation values must be distinct.
151 std::set<uint32_t> obsValues;
152 for (const auto& [key, val] : obsMap) {
153 obsValues.insert(val);
154 }
155 EXPECT_EQ(obsMap.size(), obsValues.size());
156}
157
158TEST(MonitorVerifier, RestartSemanticsHasNoSink) {
159 auto mc = buildSimpleMC();
160 auto monitor = buildSimpleMonitor();
161
163 options.useRestartSemantics = true;
164
165 auto mv = makeProduct(mc, monitor, options);
166 const auto& labeling = mv->getProduct().getStateLabeling();
167
168 // With rejection sampling there is no dedicated sink state.
169 EXPECT_FALSE(labeling.containsLabel("sink"));
170
171 // But goal / stop / init must still be present.
172 EXPECT_TRUE(labeling.containsLabel("goal"));
173 EXPECT_TRUE(labeling.containsLabel("stop"));
174 EXPECT_TRUE(labeling.containsLabel("init"));
175}
176
177TEST(MonitorVerifier, WithRiskAlmostOneGoesToGoal) {
178 auto mc = buildSimpleMC();
179 auto monitor = buildSimpleMonitor();
180
182 options.useRestartSemantics = false;
183
184 auto exprManager = std::make_shared<storm::expressions::ExpressionManager>();
185 storm::generator::GenerateMonitorVerifier<double> gen(mc, monitor, exprManager, options);
186
187 // s0=0.0, s1=1.0 (goes entirely to goal), s2=0.0 (goes entirely to stop)
188 gen.setRisk({0.0, 1.0, 0.0});
189 auto mv = gen.createProduct();
190
191 const auto& product = mv->getProduct();
192 const auto& labeling = product.getStateLabeling();
193 const auto& transMatrix = product.getTransitionMatrix();
194
195 uint64_t goalState = *labeling.getStates("goal").begin();
196 uint64_t stopState = *labeling.getStates("stop").begin();
197 uint32_t acceptingObs = mv->getObservationMap().at(std::make_pair(1u, true));
198
199 bool foundGoalOnly = false; // risk=1 accepting state -> only to goal
200 bool foundStopOnly = false; // risk=0 accepting state -> only to stop
201
202 for (uint64_t state = 0; state < product.getNumberOfStates(); ++state) {
203 if (product.getObservation(state) != acceptingObs)
204 continue;
205 for (uint64_t row = transMatrix.getRowGroupIndices()[state]; row < transMatrix.getRowGroupIndices()[state + 1]; ++row) {
206 if (product.getChoiceLabeling().getLabelsOfChoice(row).count("end") == 0)
207 continue;
208 bool toGoal = false, toStop = false;
209 for (const auto& entry : transMatrix.getRow(row)) {
210 if (entry.getColumn() == goalState)
211 toGoal = true;
212 if (entry.getColumn() == stopState)
213 toStop = true;
214 }
215 if (toGoal && !toStop)
216 foundGoalOnly = true;
217 if (!toGoal && toStop)
218 foundStopOnly = true;
219 }
220 }
221
222 EXPECT_TRUE(foundGoalOnly) << "Expected an accepting state with risk=1 to route entirely to goal";
223 EXPECT_TRUE(foundStopOnly) << "Expected an accepting state with risk=0 to route entirely to stop";
224}
225
226TEST(MonitorVerifier, WithRiskIntermediateSplitsGoalStop) {
227 auto mc = buildSimpleMC();
228 auto monitor = buildSimpleMonitor();
229
231 options.useRestartSemantics = false;
232
233 auto exprManager = std::make_shared<storm::expressions::ExpressionManager>();
234 storm::generator::GenerateMonitorVerifier<double> gen(mc, monitor, exprManager, options);
235
236 // s1 has risk 0.5 — the end-transition should go to both goal and stop.
237 gen.setRisk({0.0, 0.5, 0.0});
238 auto mv = gen.createProduct();
239
240 const auto& product = mv->getProduct();
241 const auto& labeling = product.getStateLabeling();
242 const auto& transMatrix = product.getTransitionMatrix();
243
244 uint64_t goalState = *labeling.getStates("goal").begin();
245 uint64_t stopState = *labeling.getStates("stop").begin();
246 uint32_t acceptingObs = mv->getObservationMap().at(std::make_pair(1u, true));
247
248 bool foundSplit = false;
249
250 for (uint64_t state = 0; state < product.getNumberOfStates(); ++state) {
251 if (product.getObservation(state) != acceptingObs)
252 continue;
253 for (uint64_t row = transMatrix.getRowGroupIndices()[state]; row < transMatrix.getRowGroupIndices()[state + 1]; ++row) {
254 if (product.getChoiceLabeling().getLabelsOfChoice(row).count("end") == 0)
255 continue;
256 bool toGoal = false, toStop = false;
257 for (const auto& entry : transMatrix.getRow(row)) {
258 if (entry.getColumn() == goalState)
259 toGoal = true;
260 if (entry.getColumn() == stopState)
261 toStop = true;
262 }
263 if (toGoal && toStop) {
264 foundSplit = true;
265 }
266 }
267 }
268
269 EXPECT_TRUE(foundSplit) << "Expected an accepting state with risk=0.5 to split between goal and stop";
270}
271
272} // namespace
TEST(OrderTest, Simple)
Definition OrderTest.cpp:15
This class manages the labeling of the choice space with a number of (atomic) labels.
This class represents a discrete-time Markov chain.
Definition Dtmc.h:13
This class represents a (discrete-time) Markov decision process.
Definition Mdp.h:13
This class manages the labeling of the state space with a number of (atomic) labels.
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
A class that can be used to build a sparse matrix by adding value by value.