Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
ValuationsTest.cpp
Go to the documentation of this file.
1#include <ranges>
2
3#include "storm-config.h"
4#include "test/storm_gtest.h"
5
17
18TEST(ValuationTest, StateValuationConstruction) {
19#ifndef STORM_HAVE_Z3
20 GTEST_SKIP() << "Z3 not available.";
21#endif
22 storm::prism::Program program = storm::parser::PrismParser::parse(STORM_TEST_RESOURCES_DIR "/dtmc/die.pm");
24 generatorOptions.setBuildStateValuations();
25 generatorOptions.setBuildAllLabels();
26 auto builder = storm::builder::ExplicitModelBuilder<double>(program, generatorOptions);
27 std::shared_ptr<storm::models::sparse::Model<double>> model = builder.build();
28 ASSERT_TRUE(model->hasStateValuations());
29 auto const& sv = model->getStateValuations();
30 ASSERT_EQ(sv.getNumberOfEntities(), model->getNumberOfStates());
31 ASSERT_TRUE(sv.getManager().hasVariable("s"));
32 ASSERT_TRUE(sv.getManager().hasVariable("d"));
33 auto const s = sv.getManager().getVariable("s");
34 auto const d = sv.getManager().getVariable("d");
35 auto const vars = sv.getAllVariables();
36 ASSERT_EQ(2, vars.size());
37 ASSERT_TRUE(vars.contains(s));
38 ASSERT_TRUE(vars.contains(d));
39 // reading values at sinit
40 uint64_t const sinit = *model->getInitialStates().begin();
41 ASSERT_TRUE(sv.entityHasVariable(sinit, s));
42 ASSERT_TRUE(sv.entityHasVariable(sinit, d));
43 EXPECT_EQ(0, sv.getInt64Value(sinit, s));
44 EXPECT_EQ(0, sv.getOptionalInt64Value(sinit, s).value());
45 EXPECT_EQ(0, sv.getOptionalInt64Value(sinit, d).value());
46 // reading json at sinit
47 auto js = sv.toJson(sinit);
48 EXPECT_EQ(2, js.size());
49 EXPECT_TRUE(js.contains("s"));
50 EXPECT_TRUE(js.contains("d"));
51 EXPECT_EQ(0, js["s"].get<int64_t>());
52 EXPECT_EQ(0, js["d"].get<int64_t>());
53 // reading values at "three" state
54 ASSERT_TRUE(model->getStateLabeling().containsLabel("three"));
55 ASSERT_TRUE(model->getStates("three").hasUniqueSetBit());
56 uint64_t const three = *model->getStates("three").begin();
57 EXPECT_EQ(7, sv.getInt64Value(three, s));
58 EXPECT_EQ(3, sv.getInt64Value(three, d));
59 // reading all values for d
60 auto dValues = sv.getInt64Values(d);
61 ASSERT_EQ(sv.getNumberOfEntities(), dValues.size());
62 EXPECT_EQ(3, dValues[three]);
63 int64_t const sum = std::accumulate(dValues.begin(), dValues.end(), 0ll);
64 EXPECT_EQ(1 + 2 + 3 + 4 + 5 + 6, sum);
65}
66
67TEST(ValuationTest, StateValuationTransformation) {
68#ifndef STORM_HAVE_Z3
69 GTEST_SKIP() << "Z3 not available.";
70#endif
71 storm::prism::Program program = storm::parser::PrismParser::parse(STORM_TEST_RESOURCES_DIR "/dtmc/die.pm");
73 generatorOptions.setBuildStateValuations();
74 auto builder = storm::builder::ExplicitModelBuilder<double>(program, generatorOptions);
75 std::shared_ptr<storm::models::sparse::Model<double>> model = builder.build();
76 ASSERT_TRUE(model->hasStateValuations());
77 auto const& sv = model->getStateValuations();
79 auto newsv = notransformer.build(true);
80 ASSERT_EQ(newsv.getNumberOfEntities(), sv.getNumberOfEntities());
82 auto const svar = program.getManager().getVariable("s");
83 auto const dvar = program.getManager().getVariable("d");
84 auto const sgt3Var = program.getManager().declareBooleanVariable("sGT3");
85 auto const alwaysTrueVar = program.getManager().declareBooleanVariable("alwaysTrue");
86 auto const alwaysFalseVar = program.getManager().declareBooleanVariable("alwaysFalse");
87 transformer.addExpression(sgt3Var, svar.getExpression() > program.getManager().integer(3));
88 transformer.addExpression(alwaysTrueVar, svar.getExpression() == svar.getExpression());
89 transformer.addExpression(alwaysFalseVar, dvar.getExpression() < dvar.getExpression());
90 newsv = transformer.build(true);
91 auto const vars = newsv.getAllVariables();
92 ASSERT_EQ(5, vars.size());
93 ASSERT_TRUE(vars.contains(svar));
94 ASSERT_TRUE(vars.contains(dvar));
95 ASSERT_TRUE(vars.contains(sgt3Var));
96 ASSERT_TRUE(vars.contains(alwaysTrueVar));
97 ASSERT_TRUE(vars.contains(alwaysFalseVar));
98 uint64_t const sinit = *model->getInitialStates().begin();
99 EXPECT_EQ(0, newsv.getInt64Value(sinit, svar));
100 EXPECT_EQ(0, newsv.getInt64Value(sinit, dvar));
101 EXPECT_FALSE(newsv.getBooleanValue(sinit, sgt3Var));
102 EXPECT_TRUE(newsv.getBooleanValue(sinit, alwaysTrueVar));
103 EXPECT_FALSE(newsv.getBooleanValue(sinit, alwaysFalseVar));
104
105 for (uint64_t state = 0; state < newsv.getNumberOfEntities(); ++state) {
106 ASSERT_TRUE(newsv.getBooleanValue(state, alwaysTrueVar));
107 ASSERT_FALSE(newsv.getBooleanValue(state, alwaysFalseVar));
108 ASSERT_EQ(sv.getInt64Value(state, svar), newsv.getInt64Value(state, svar));
109 ASSERT_EQ(newsv.getBooleanValue(state, sgt3Var), newsv.getInt64Value(state, svar) > 3);
110 }
111}
112
113TEST(ValuationTest, Valuations2classes) {
114 auto manager = std::make_shared<storm::expressions::ExpressionManager>();
115 auto const b = manager->declareBooleanVariable("b");
116 auto const i = manager->declareIntegerVariable("i");
117 auto const s = manager->declareStringVariable("s");
118 auto const r = manager->declareRationalVariable("r");
119 std::vector<storm::storage::sparse::ValuationClassDescription> classes;
120 {
122 builder1.addBooleanVariable(b, true); // 1 + 1 bit (optional
123 builder1.addIntegerVariable(i, -4, 12); // 17 different values, therefore 5 bits
124 builder1.addStringVariable(s, true); // 64 + 1 bits (optional)
125 builder1.addRationalVariable(r, 166); // 166 bits
126 classes.push_back(builder1.buildClassDescription()); // adds 2 padding bits to fill a whole number of bytes
127 EXPECT_EQ(2 + 5 + 65 + 166 + 2, classes.back().sizeInBits());
128 EXPECT_TRUE(classes.back().hasStringVariable());
129
131 builder2.addDoubleVariable(r); // 64 bits
132 builder2.addBooleanVariable(b); // 1 bit
133 builder2.addIntegerVariable(i, -10, -7); // 4 different values, therefore 2 bits
134 classes.push_back(builder2.buildClassDescription()); // adds 5 padding bits to fill a whole number of bytes
135 EXPECT_EQ(64 + 1 + 2 + 5, classes.back().sizeInBits());
136 EXPECT_FALSE(classes.back().hasStringVariable());
137 }
138 storm::storage::sparse::ValuationsStorage valuations(classes, {manager, manager});
139 EXPECT_EQ(0, valuations.numStrings());
140 EXPECT_EQ(2, valuations.numClasses());
141 EXPECT_EQ(classes[0].sizeInBits(), valuations.getClassDescription(0).sizeInBits());
142 EXPECT_EQ(classes[1].sizeInBits(), valuations.getClassDescription(1).sizeInBits());
143 auto const vars = valuations.getAllVariables();
144 EXPECT_EQ(4, vars.size());
145 EXPECT_TRUE(vars.contains(b));
146 EXPECT_TRUE(vars.contains(i));
147 EXPECT_TRUE(vars.contains(s));
148 EXPECT_TRUE(vars.contains(r));
149 // Insert 200 entities with alternating classes and some non-trivial values
150 std::vector<std::optional<bool>> b_values;
151 std::vector<int64_t> i_values;
152 std::vector<std::optional<std::string>> s_values;
153 std::vector<storm::RationalNumber> r_values;
154 for (uint64_t e = 0; e < 200; ++e) {
155 if (e % 3 == 0) {
156 // insert class 0
157 valuations.emplaceBack<true>(0, [&](auto entity, auto const& var, auto& value) {
158 using ValueType = std::remove_cvref_t<decltype(value)>;
159 if constexpr (std::is_same_v<ValueType, std::optional<bool>>) {
160 EXPECT_EQ(b, var);
161 if (entity % 2 == 0) {
162 value = entity % 4 == 0;
163 }
164 b_values.push_back(value);
165 } else if constexpr (std::is_same_v<ValueType, int64_t>) {
166 EXPECT_EQ(i, var);
167 value = static_cast<int64_t>(entity) % 17 - 4;
168 i_values.push_back(value);
169 } else if constexpr (std::is_same_v<ValueType, std::optional<std::string>>) {
170 EXPECT_EQ(s, var);
171 if (entity % 2 == 1) {
172 value = "str" + std::to_string(entity);
173 }
174 s_values.push_back(value);
175 } else if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
176 EXPECT_EQ(r, var);
177 auto const uint64max = storm::utility::convertNumber<storm::RationalNumber, uint64_t>(std::numeric_limits<uint64_t>::max());
178 value = (uint64max + uint64max + storm::utility::convertNumber<storm::RationalNumber>(entity)) / (uint64max);
179 if (entity % 8 == 0) {
180 value = -value;
181 }
182 r_values.push_back(value);
183 } else {
184 FAIL() << "Unexpected variable type " << typeid(ValueType).name() << " for variable " << var.getName();
185 }
186 });
187 } else {
188 // insert class 1
189 valuations.emplaceBack<false, double, bool, int64_t>(1, [&](auto entity, auto const& var, auto& value) {
190 using ValueType = std::remove_cvref_t<decltype(value)>;
191 if constexpr (std::is_same_v<ValueType, double>) {
192 EXPECT_EQ(r, var);
193 value = static_cast<double>(entity) / 3.0;
195 } else if constexpr (std::is_same_v<ValueType, bool>) {
196 EXPECT_EQ(b, var);
197 value = entity % 2 == 0;
198 b_values.push_back(value);
199 } else {
200 static_assert(std::is_same_v<ValueType, int64_t>);
201 EXPECT_EQ(i, var);
202 value = static_cast<int64_t>(entity) % 4 - 10;
203 i_values.push_back(value);
204 }
205 });
206 s_values.push_back(std::nullopt);
207 }
208 }
209 // Now check if reading back the values works correctly.
210 ASSERT_EQ(200, b_values.size());
211 ASSERT_EQ(200, i_values.size());
212 ASSERT_EQ(200, s_values.size());
213 ASSERT_EQ(200, r_values.size());
214 valuations.readCallback<std::nullopt_t, bool, int64_t, std::string, storm::RationalNumber, double>([&](auto entity, auto const& var, auto const& value) {
215 using ValueType = std::remove_cvref_t<decltype(value)>;
216 if constexpr (std::is_same_v<ValueType, std::nullopt_t>) {
217 EXPECT_EQ(0, valuations.getClassOfEntity(entity));
218 if (var == b) {
219 EXPECT_FALSE(b_values[entity].has_value());
220 } else {
221 EXPECT_TRUE(var == s);
222 EXPECT_FALSE(s_values[entity].has_value());
223 }
224 } else if constexpr (std::is_same_v<ValueType, bool>) {
225 EXPECT_EQ(b, var);
226 ASSERT_TRUE(b_values[entity].has_value());
227 EXPECT_EQ(b_values[entity].value(), value);
228 } else if constexpr (std::is_same_v<ValueType, int64_t>) {
229 EXPECT_EQ(i, var);
230 EXPECT_EQ(i_values[entity], value);
231 } else if constexpr (std::is_same_v<ValueType, std::string>) {
232 EXPECT_EQ(s, var);
233 ASSERT_TRUE(s_values[entity].has_value());
234 EXPECT_EQ(s_values[entity].value(), value);
235 } else if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
236 EXPECT_EQ(0, valuations.getClassOfEntity(entity));
237 EXPECT_EQ(r, var);
238 EXPECT_EQ(r_values[entity], value);
239 } else {
240 static_assert(std::is_same_v<ValueType, double>);
241 EXPECT_EQ(1, valuations.getClassOfEntity(entity));
242 EXPECT_EQ(r, var);
244 }
245 });
246}
247
248TEST(ValuationTest, ValuationsSingleClass) {
249 // Tests point-access via readValue / writeValue, entityHasVariable, getAllVariables, and resize
250 // on a simple single-class layout with non-optional variables only.
251 auto manager = std::make_shared<storm::expressions::ExpressionManager>();
252 auto const b = manager->declareBooleanVariable("b");
253 auto const i = manager->declareIntegerVariable("i");
254 auto const d = manager->declareRationalVariable("d");
255
257 builder.addBooleanVariable(b); // 1 bit
258 builder.addIntegerVariable(i, -5, 5); // 11 values → 4 bits
259 builder.addDoubleVariable(d); // 64 bits
260 auto const desc = builder.buildClassDescription();
261 EXPECT_EQ(1 + 4 + 64 + 3, desc.sizeInBits());
262
263 storm::storage::sparse::ValuationsStorage valuations(desc, manager);
264 EXPECT_EQ(0u, valuations.size());
265 EXPECT_EQ(1u, valuations.numClasses());
266
267 // getAllVariables should list exactly the three declared variables
268 auto const vars = valuations.getAllVariables();
269 EXPECT_EQ(3u, vars.size());
270 EXPECT_TRUE(vars.contains(b));
271 EXPECT_TRUE(vars.contains(i));
272 EXPECT_TRUE(vars.contains(d));
273
274 // Insert 6 entities with distinct, easily checkable values
275 std::vector<bool> bVals = {true, false, true, false, true, false};
276 std::vector<int64_t> iVals = {-5, -3, 0, 2, 4, 5};
277 std::vector<double> dVals = {0.0, 1.5, -2.25, 1e10, -1e-5, 3.14};
278
279 for (uint64_t e = 0; e < 6; ++e) {
280 valuations.emplaceBack<false, bool, int64_t, double>([&](auto entity, auto const& var, auto& value) {
281 using ValueType = std::remove_cvref_t<decltype(value)>;
282 if constexpr (std::is_same_v<ValueType, bool>) {
283 value = bVals[entity];
284 } else if constexpr (std::is_same_v<ValueType, int64_t>) {
285 value = iVals[entity];
286 } else {
287 static_assert(std::is_same_v<ValueType, double>);
288 value = dVals[entity];
289 }
290 });
291 }
292 ASSERT_EQ(6u, valuations.size());
293
294 // entityHasVariable: all variables belong to the single class, so always true
295 for (uint64_t e = 0; e < 6; ++e) {
296 EXPECT_TRUE(valuations.entityHasVariable(e, b));
297 EXPECT_TRUE(valuations.entityHasVariable(e, i));
298 EXPECT_TRUE(valuations.entityHasVariable(e, d));
299 }
300
301 // readValue round-trips
302 for (uint64_t e = 0; e < 6; ++e) {
303 EXPECT_EQ(bVals[e], valuations.readValue<bool>(e, b)) << " at entity " << e;
304 EXPECT_EQ(iVals[e], valuations.readValue<int64_t>(e, i)) << " at entity " << e;
305 EXPECT_EQ(dVals[e], valuations.readValue<double>(e, d)) << " at entity " << e;
306 }
307
308 // writeValue then readValue: overwrite entity 3 and verify neighbours are unaffected
309 valuations.writeValue(3, b, true);
310 valuations.writeValue(3, i, int64_t(-1));
311 valuations.writeValue(3, d, 99.0);
312 EXPECT_EQ(true, valuations.readValue<bool>(3, b));
313 EXPECT_EQ(-1, valuations.readValue<int64_t>(3, i));
314 EXPECT_EQ(99.0, valuations.readValue<double>(3, d));
315 // neighbours untouched
316 EXPECT_EQ(bVals[2], valuations.readValue<bool>(2, b));
317 EXPECT_EQ(iVals[4], valuations.readValue<int64_t>(4, i));
318
319 // resize: grow to 9 — new entities get default values and the first 6 stay intact
320 valuations.resize(9);
321 ASSERT_EQ(9u, valuations.size());
322 for (uint64_t e = 0; e < 6; ++e) {
323 if (e == 3) {
324 continue; // entity 3 was overwritten above
325 }
326 EXPECT_EQ(bVals[e], valuations.readValue<bool>(e, b)) << " at entity " << e;
327 EXPECT_EQ(iVals[e], valuations.readValue<int64_t>(e, i)) << " at entity " << e;
328 EXPECT_EQ(dVals[e], valuations.readValue<double>(e, d)) << " at entity " << e;
329 }
330
331 // resize: shrink back to 4
332 valuations.resize(4);
333 EXPECT_EQ(4u, valuations.size());
334 EXPECT_EQ(bVals[0], valuations.readValue<bool>(0, b));
335 EXPECT_EQ(iVals[1], valuations.readValue<int64_t>(1, i));
336 EXPECT_EQ(dVals[2], valuations.readValue<double>(2, d));
337}
338
339TEST(ValuationTest, ValuationsSelectEntities) {
340 // Tests selectEntities (BitVector and vector<uint64_t> overloads) on a single-class
341 // layout. Verifies that the selection preserves values in the correct order and that the
342 // original Valuations object is left unchanged.
343 auto manager = std::make_shared<storm::expressions::ExpressionManager>();
344 auto const b = manager->declareBooleanVariable("b");
345 auto const i = manager->declareIntegerVariable("i");
346
348 builder.addBooleanVariable(b); // 1 bit
349 builder.addIntegerVariable(i, 0, 7); // 8 values → 3 bits
350 auto const desc = builder.buildClassDescription();
351
352 storm::storage::sparse::ValuationsStorage valuations(desc, manager);
353
354 // Insert 8 entities: b = (entity % 2 == 0), i = entity
355 for (uint64_t e = 0; e < 8; ++e) {
356 valuations.emplaceBack<false, bool, int64_t>([e](auto /*entity*/, auto const& var, auto& value) {
357 using ValueType = std::remove_cvref_t<decltype(value)>;
358 if constexpr (std::is_same_v<ValueType, bool>) {
359 value = (e % 2 == 0);
360 } else {
361 static_assert(std::is_same_v<ValueType, int64_t>);
362 value = static_cast<int64_t>(e);
363 }
364 });
365 }
366 ASSERT_EQ(8u, valuations.size());
367
368 // --- BitVector selection: pick entities 1, 3, 5, 7 (odd indices) ---
369 storm::storage::BitVector bvOdd(8, false);
370 for (uint64_t e = 1; e < 8; e += 2) {
371 bvOdd.set(e);
372 }
373 auto const selectedBv = valuations.selectEntities(bvOdd);
374 ASSERT_EQ(4u, selectedBv.size());
375 EXPECT_EQ(1u, selectedBv.numClasses());
376 for (uint64_t sel = 0; sel < 4; ++sel) {
377 uint64_t const origEntity = 2 * sel + 1; // 1, 3, 5, 7
378 EXPECT_EQ(false, selectedBv.readValue<bool>(sel, b)) << " selected entity " << sel;
379 EXPECT_EQ(static_cast<int64_t>(origEntity), selectedBv.readValue<int64_t>(sel, i)) << " selected entity " << sel;
380 }
381
382 // --- vector<uint64_t> selection: pick entities {6, 0, 4} in that order ---
383 std::vector<uint64_t> const indices = {6, 0, 4};
384 auto const selectedVec = valuations.selectEntities(indices);
385 ASSERT_EQ(3u, selectedVec.size());
386 EXPECT_EQ(true, selectedVec.readValue<bool>(0, b)); // entity 6: even → true
387 EXPECT_EQ(6, selectedVec.readValue<int64_t>(0, i));
388 EXPECT_EQ(true, selectedVec.readValue<bool>(1, b)); // entity 0: even → true
389 EXPECT_EQ(0, selectedVec.readValue<int64_t>(1, i));
390 EXPECT_EQ(true, selectedVec.readValue<bool>(2, b)); // entity 4: even → true
391 EXPECT_EQ(4, selectedVec.readValue<int64_t>(2, i));
392
393 // Original must be unchanged
394 ASSERT_EQ(8u, valuations.size());
395 for (uint64_t e = 0; e < 8; ++e) {
396 EXPECT_EQ(e % 2 == 0, valuations.readValue<bool>(e, b)) << " at original entity " << e;
397 EXPECT_EQ(static_cast<int64_t>(e), valuations.readValue<int64_t>(e, i)) << " at original entity " << e;
398 }
399}
400
401TEST(ValuationTest, RejectsNonCompliantDoubleOrStringSize) {
402 // Double and String fields must always be exactly their default size (64 bits) per the UMB spec
403 // (see storm::umb::validation::validateTypeDeclaration).
404 auto manager = std::make_shared<storm::expressions::ExpressionManager>();
405 auto const d = manager->declareRationalVariable("d");
408 .name = "d", .isOptional = std::nullopt, .type = {storm::umb::Type::Double, 128}, .lower = {}, .upper = {}, .offset = {}};
409 EXPECT_THROW(doubleBuilder.addVariable(badDouble), storm::exceptions::WrongFormatException);
410
411 auto const s = manager->declareStringVariable("s");
414 .name = "s", .isOptional = std::nullopt, .type = {storm::umb::Type::String, 128}, .lower = {}, .upper = {}, .offset = {}};
415 EXPECT_THROW(stringBuilder.addVariable(badString), storm::exceptions::WrongFormatException);
416
417 // A compliant (default-size) Double field must still be accepted.
420 .name = "d", .isOptional = std::nullopt, .type = {storm::umb::Type::Double, std::nullopt}, .lower = {}, .upper = {}, .offset = {}};
421 EXPECT_NO_THROW(okBuilder.addVariable(okDouble));
422}
423
424TEST(ValuationTest, OverwriteValues) {
425 // Overwriting values must not affect neighboring variables (variables are not byte-aligned and span multiple bytes)
426 auto manager = std::make_shared<storm::expressions::ExpressionManager>();
427 auto const a = manager->declareIntegerVariable("a");
428 auto const b = manager->declareIntegerVariable("b");
429 auto const c = manager->declareIntegerVariable("c");
431 descBuilder.addIntegerVariable(a, 0, 7); // 3 bits
432 descBuilder.addIntegerVariable(b, 0, 100000); // 17 bits, crosses byte boundaries
433 descBuilder.addIntegerVariable(c, 0, 1000); // 10 bits
435 storage.resize(1);
436 for (int64_t round = 0; round < 3; ++round) {
437 int64_t const av = round == 0 ? 7 : (round == 1 ? 0 : 5);
438 int64_t const bv = round == 0 ? 99999 : (round == 1 ? 1 : 65537);
439 int64_t const cv = round == 0 ? 1000 : (round == 1 ? 0 : 513);
440 storage.writeValue<int64_t>(0, a, av);
441 storage.writeValue<int64_t>(0, b, bv);
442 storage.writeValue<int64_t>(0, c, cv);
443 EXPECT_EQ(av, storage.readValue<int64_t>(0, a));
444 EXPECT_EQ(bv, storage.readValue<int64_t>(0, b));
445 EXPECT_EQ(cv, storage.readValue<int64_t>(0, c));
446 }
447}
448
449TEST(ValuationTest, ResizeInitializesAllNewEntitiesWithDefaults) {
450 auto manager = std::make_shared<storm::expressions::ExpressionManager>();
451 auto const i = manager->declareIntegerVariable("i");
452 auto const r = manager->declareRationalVariable("r");
453 auto const b = manager->declareBooleanVariable("b");
455 // The default value of i is its upper bound -3, whose bit representation is not zero
456 descBuilder.addVariable(
457 {.name = "i", .isOptional = std::nullopt, .type = {storm::umb::Type::Int, 8}, .lower = std::nullopt, .upper = -3, .offset = std::nullopt});
458 // The default value of r is 0/1 (and not 0/0)
459 descBuilder.addRationalVariable(r, 16);
460 descBuilder.addBooleanVariable(b);
462
463 storage.resize(5);
464 ASSERT_EQ(5u, storage.size());
465 for (uint64_t entity = 0; entity < 5; ++entity) {
466 EXPECT_EQ(-3, storage.readValue<int64_t>(entity, i)) << "entity " << entity;
467 EXPECT_EQ(storm::RationalNumber(0), storage.readValue<storm::RationalNumber>(entity, r)) << "entity " << entity;
468 EXPECT_FALSE(storage.readValue<bool>(entity, b)) << "entity " << entity;
469 }
470 // Growing an already non-empty storage keeps the existing entities
471 storage.writeValue<int64_t>(2, i, -7);
472 storage.resize(8);
473 ASSERT_EQ(8u, storage.size());
474 EXPECT_EQ(-7, storage.readValue<int64_t>(2, i));
475 for (uint64_t entity : {0, 1, 3, 4, 5, 6, 7}) {
476 EXPECT_EQ(-3, storage.readValue<int64_t>(entity, i)) << "entity " << entity;
477 EXPECT_EQ(storm::RationalNumber(0), storage.readValue<storm::RationalNumber>(entity, r)) << "entity " << entity;
478 }
479 // Shrinking
480 storage.resize(3);
481 EXPECT_EQ(3u, storage.size());
482 EXPECT_EQ(-7, storage.readValue<int64_t>(2, i));
483}
484
485TEST(ValuationTest, ResizeMultipleClasses) {
486 auto manager = std::make_shared<storm::expressions::ExpressionManager>();
487 auto const a = manager->declareIntegerVariable("a");
488 auto const b = manager->declareIntegerVariable("b");
489 std::vector<storm::storage::sparse::ValuationClassDescription> classes;
490 {
492 builder.addIntegerVariable(a, 0, 7); // 3 bits, padded to one byte
493 classes.push_back(builder.buildClassDescription());
494 }
495 {
497 builder.addIntegerVariable(b, -20000, 20000); // 16 bits
498 classes.push_back(builder.buildClassDescription());
499 }
500 storm::storage::sparse::ValuationsStorage storage(classes, {manager});
501 ASSERT_EQ(2u, storage.numClasses());
502
503 storage.resize(3, 0);
504 storage.resize(7, 1);
505 ASSERT_EQ(7u, storage.size());
506 for (uint64_t entity = 0; entity < 7; ++entity) {
507 EXPECT_EQ(entity < 3 ? 0u : 1u, storage.getClassOfEntity(entity)) << "entity " << entity;
508 }
509 // Make sure that we can access all entities independently of each other (in particular the last one)
510 for (uint64_t entity = 0; entity < 3; ++entity) {
511 storage.writeValue<int64_t>(entity, a, entity + 1);
512 }
513 for (uint64_t entity = 3; entity < 7; ++entity) {
514 storage.writeValue<int64_t>(entity, b, -static_cast<int64_t>(entity) * 1000);
515 }
516 for (uint64_t entity = 0; entity < 3; ++entity) {
517 EXPECT_EQ(static_cast<int64_t>(entity + 1), storage.readValue<int64_t>(entity, a));
518 }
519 for (uint64_t entity = 3; entity < 7; ++entity) {
520 EXPECT_EQ(-static_cast<int64_t>(entity) * 1000, storage.readValue<int64_t>(entity, b));
521 }
522 EXPECT_EQ(3u + 4u * 2u, storage.getRawUmbData().valuations->size());
523}
524
525TEST(ValuationTest, WriteRejectsValuesThatDoNotFitIntoStoredType) {
526 auto manager = std::make_shared<storm::expressions::ExpressionManager>();
527 auto const s = manager->declareIntegerVariable("s");
528 auto const u = manager->declareIntegerVariable("u");
530 // Signed and unsigned 4 bit variables without declared bounds
531 descBuilder.addVariable({.name = "s", .isOptional = std::nullopt, .type = {storm::umb::Type::Int, 4}, .lower = {}, .upper = {}, .offset = {}});
532 descBuilder.addVariable({.name = "u", .isOptional = std::nullopt, .type = {storm::umb::Type::Uint, 4}, .lower = {}, .upper = {}, .offset = {}});
534 storage.resize(1);
535 using storm::exceptions::OutOfRangeException;
537
538 for (int64_t v : std::views::iota(-8, 8)) {
539 storage.writeValue<int64_t>(0, s, v);
540 EXPECT_EQ(v, storage.readValue<int64_t>(0, s));
541 }
542 for (int64_t v : {-9, -100, 8, 15, 16}) {
543 STORM_SILENT_EXPECT_THROW(storage.writeValue<int64_t>(0, s, v), OutOfRangeException);
544 STORM_SILENT_EXPECT_THROW(storage.writeValue<Integer>(0, s, Integer(static_cast<long>(v))), OutOfRangeException);
545 }
546 // The failed writes did not modify the value
547 storage.writeValue<int64_t>(0, s, -8);
548 STORM_SILENT_EXPECT_THROW(storage.writeValue<int64_t>(0, s, 8), OutOfRangeException);
549 EXPECT_EQ(-8, storage.readValue<int64_t>(0, s));
550
551 for (int64_t v : std::views::iota(0, 16)) {
552 storage.writeValue<int64_t>(0, u, v);
553 EXPECT_EQ(v, storage.readValue<int64_t>(0, u));
554 }
555 for (int64_t v : {-1, -16, 16, 17}) {
556 STORM_SILENT_EXPECT_THROW(storage.writeValue<int64_t>(0, u, v), OutOfRangeException);
557 }
558 STORM_SILENT_EXPECT_THROW(storage.writeValue<Integer>(0, u, Integer(-1)), OutOfRangeException);
559}
560
561TEST(ValuationTest, WriteAcceptsFullRangeInt64UintVariable) {
562 auto manager = std::make_shared<storm::expressions::ExpressionManager>();
563 auto const v = manager->declareIntegerVariable("v");
565 descBuilder.addIntegerVariable(v, std::numeric_limits<int64_t>::min(), std::numeric_limits<int64_t>::max());
567 storage.resize(1);
568
569 for (int64_t const val :
570 {int64_t(0), int64_t(1), int64_t(5), std::numeric_limits<int64_t>::max(), int64_t(-1), int64_t(-5), std::numeric_limits<int64_t>::min()}) {
571 storage.writeValue<int64_t>(0, v, val);
572 EXPECT_EQ(val, storage.readValue<int64_t>(0, v)) << "Round-trip failed for value " << val;
573 }
574}
575
576TEST(ValuationTest, FailedWriteDoesNotSetPresenceBitOfOptionalVariable) {
577 auto manager = std::make_shared<storm::expressions::ExpressionManager>();
578 auto const i = manager->declareIntegerVariable("i");
580 descBuilder.addIntegerVariable(i, 0, 5, true);
582 storage.emplaceBack<true>([](auto, auto, auto&) { /* leave optional variable unset */ });
583 auto isSet = [&storage, &i]() {
584 bool result = false;
585 storage.readCallback<std::nullopt_t, int64_t>(
586 0, i, [&result](auto, auto, auto const& value) { result = !std::is_same_v<std::remove_cvref_t<decltype(value)>, std::nullopt_t>; });
587 return result;
588 };
589 ASSERT_FALSE(isSet());
590 // Out of the variable's bounds
591 auto writeOutOfBounds = [&storage, &i]() { storage.writeCallback<false, true, int64_t>(0, i, [](auto, auto, auto& value) { value = 10; }); };
592 STORM_SILENT_EXPECT_THROW(writeOutOfBounds(), storm::exceptions::OutOfRangeException);
593 EXPECT_FALSE(isSet());
594 // Successful write
595 storage.writeCallback<false, true, int64_t>(0, i, [](auto, auto, auto& value) { value = 3; });
596 ASSERT_TRUE(isSet());
597 EXPECT_EQ(3, storage.readValue<int64_t>(0, i));
598}
double round(double val, int precision)
TEST(ValuationTest, StateValuationConstruction)
BuilderOptions & setBuildAllLabels(bool newValue=true)
Should all reward models be built?
BuilderOptions & setBuildStateValuations(bool newValue=true)
Should the state valuation mapping be built?
Variable declareBooleanVariable(std::string const &name, bool auxiliary=false)
Declares a new boolean variable with a name that must not yet exist and its corresponding type.
Expression integer(int_fast64_t value) const
Creates an expression that characterizes the given integer literal.
Variable getVariable(std::string const &name) const
Retrieves the expression that represents the variable with the given name.
static storm::prism::Program parse(std::string const &filename, bool prismCompatability=false)
Parses the given file into the PRISM storage classes assuming it complies with the PRISM syntax.
storm::expressions::ExpressionManager & getManager() const
Retrieves the manager responsible for the expressions of this program.
Definition Program.cpp:2388
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
void set(uint64_t index, bool value=true)
Sets the given truth value at the given index.
Helper to incrementally build a ValuationClassDescription, i.e.
ValuationClassDescription buildClassDescription()
Creates the finalized state valuations object.
void addBooleanVariable(storm::expressions::Variable const &variable, bool optional=false)
Adds a new boolean variable to the builder.
void addRationalVariable(storm::expressions::Variable const &variable, uint64_t bitSize, bool optional=false)
Adds a new rational variable to the builder.
void addStringVariable(storm::expressions::Variable const &variable, bool optional=false)
Adds a new string variable to the builder.
void addIntegerVariable(storm::expressions::Variable const &variable, int64_t const lowerBound, int64_t const upperBound, bool optional=false)
Adds a new integer variable to the builder.
void addVariable(ValuationClassDescription::Variable const &variable)
Adds the given variable.
void addDoubleVariable(storm::expressions::Variable const &variable, bool optional=false)
Adds a new double variable to the builder.
Transforms the given state valuations to a new state valuations over a new variable set.
Valuations build(bool extend)
Build and export the state valuations.
void addExpression(storm::expressions::Variable const &var, storm::expressions::Expression const &expr)
Add a variable defined by the given expression.
std::set< storm::expressions::Variable > getAllVariables() const
Stores valuations of variables for a set of entities (e.g.
ValueType readValue(uint64_t entity, storm::expressions::Variable const &variable) const
Reads a single variable of the given entity and returns its value directly.
bool entityHasVariable(uint64_t entity, storm::expressions::Variable const &variable) const
Returns true iff the variable is relevant for the given entity's class, i.e.
ValuationsStorage selectEntities(T const &selectedEntities) const
Constructs a new ValuationsStorage containing only the selected entities, in the order they appear in...
void resize(uint64_t newEntityCount, uint64_t classIndex=0)
Resizes the entity count to newEntityCount.
std::set< storm::expressions::Variable > getAllVariables() const
Returns all expression variables that this valuation assigns values to for at least one class.
void readCallback(uint64_t entity, Callback const &callback) const
Reads all variables of the given entity and invokes callback for each one.
void writeValue(uint64_t entity, storm::expressions::Variable const &variable, ValueType const &value)
Directly writes value to the given variable of entity.
void writeCallback(uint64_t entity, Callback const &callback)
Writes all variables of the given entity by invoking callback for each one.
storm::NumberTraits< storm::RationalNumber >::IntegerType Integer
void emplaceBack(uint64_t classIndex, Callback const &callback)
Appends a new entity of the given class and populates its variables via callback.
storm::builder::BuilderOptions NextStateGeneratorOptions
TargetType convertNumber(SourceType const &number)
#define STORM_SILENT_EXPECT_THROW(statement, expected_exception)
Definition storm_gtest.h:21