Storm 1.13.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
UmbTest.cpp
Go to the documentation of this file.
1#include <filesystem>
2#include <limits>
3#include <random>
4#include <span>
5#include <vector>
6
7#include "storm-config.h"
19#include "test/storm_gtest.h"
23namespace {
24class UmbRoundTripTest : public ::testing::Test {
25 protected:
26 std::filesystem::path umbFile;
27 void removeUmbFile() {
28 if (!umbFile.empty() && std::filesystem::exists(umbFile)) {
29 std::error_code ec;
30 std::filesystem::remove(umbFile);
31 ASSERT_EQ(ec.value(), 0) << "Unable to remove temporary file " << umbFile << " for UMB round trip test: " << ec.message();
32 }
33 }
34
35 void setUpUmbFileName() {
36 std::error_code ec;
37 auto tmp = std::filesystem::temp_directory_path(ec);
38 ASSERT_EQ(ec.value(), 0) << "Unable to get temporary directory for UMB round trip test: " << ec.message();
39 std::random_device rd;
40 do {
41 umbFile = tmp / std::filesystem::path("storm_umb_round_trip_test_" + std::to_string(rd()) + ".umb");
42 } while (std::filesystem::exists(umbFile));
43 }
44
45 template<typename ValueType>
46 void run(std::filesystem::path const& prismfile, std::string const& constants = "", storm::umb::ExportOptions const& exportOptions = {}) {
47 setUpUmbFileName();
48 // Set-up options structs
49 storm::umb::ImportOptions importOptions;
50 importOptions.buildChoiceLabeling = exportOptions.allowChoiceLabelingAsActions || exportOptions.allowChoiceOriginsAsActions;
52 generatorOptions.setBuildChoiceLabels(exportOptions.allowChoiceLabelingAsActions);
53 generatorOptions.setBuildChoiceOrigins(exportOptions.allowChoiceOriginsAsActions);
54 generatorOptions.setBuildAllRewardModels();
55 generatorOptions.setBuildAllLabels();
56 // TODO: valuations
57
58 // build model from prism file
59 storm::prism::Program program = storm::parser::PrismParser::parse(prismfile, true);
60 program = storm::utility::prism::preprocess(program, constants);
61 auto builder = storm::builder::ExplicitModelBuilder<ValueType>(program, generatorOptions);
62 auto model = builder.build();
63
64 auto assertEqualModel = [&model](auto const& otherModelPtr) {
65 ASSERT_TRUE(otherModelPtr) << "No model.";
66 EXPECT_EQ(model->getType(), otherModelPtr->getType());
67 EXPECT_EQ(model->getNumberOfStates(), otherModelPtr->getNumberOfStates());
68 EXPECT_EQ(model->getNumberOfChoices(), otherModelPtr->getNumberOfChoices());
69 EXPECT_EQ(model->getNumberOfTransitions(), otherModelPtr->getNumberOfTransitions());
70 if (!model->isOfType(storm::models::ModelType::Ctmc) || std::is_same_v<ValueType, storm::RationalNumber>) {
71 // Round trip in CTMC case may yield slightly different probabilities because we translate between rates and probabilities
72 EXPECT_EQ(model->getTransitionMatrix(), otherModelPtr->getTransitionMatrix());
73 }
74 EXPECT_EQ(model->getStateLabeling(), otherModelPtr->getStateLabeling());
75 if (model->isNondeterministicModel() && model->hasChoiceLabeling()) {
76 // This test does not work for deterministic models as we might fuse multiple (overlapping) choice labels together
77 EXPECT_EQ(model->getChoiceLabeling(), otherModelPtr->getChoiceLabeling());
78 }
79 EXPECT_EQ(model->getNumberOfRewardModels(), otherModelPtr->getNumberOfRewardModels());
80 for (auto const& [name, rewardModel] : model->getRewardModels()) {
81 ASSERT_TRUE(otherModelPtr->hasRewardModel(name) || model->hasUniqueRewardModel()) << "Other model does not have reward model '" << name << "'.";
82 auto const& otherRewardModel = model->hasUniqueRewardModel() ? otherModelPtr->getUniqueRewardModel() : otherModelPtr->getRewardModel(name);
83 if (rewardModel.hasStateRewards()) {
84 ASSERT_TRUE(otherRewardModel.hasStateRewards());
85 EXPECT_EQ(rewardModel.getStateRewardVector(), otherRewardModel.getStateRewardVector());
86 }
87 if (rewardModel.hasStateActionRewards()) {
88 ASSERT_TRUE(otherRewardModel.hasStateActionRewards());
89 EXPECT_EQ(rewardModel.getStateActionRewardVector(), otherRewardModel.getStateActionRewardVector());
90 }
91 if (rewardModel.hasTransitionRewards()) {
92 ASSERT_TRUE(otherRewardModel.hasTransitionRewards());
93 EXPECT_EQ(rewardModel.getTransitionRewardMatrix(), otherRewardModel.getTransitionRewardMatrix());
94 }
95 }
96 };
97
98 // Short round trip: model -> umb -> model
99 auto umb1 = storm::umb::sparseModelToUmb(*model, exportOptions);
100 umb1.encodeRationals();
101 std::stringstream validationErrors;
102 ASSERT_TRUE(umb1.validate(validationErrors)) << validationErrors.str();
103 validationErrors.clear();
104 auto model1 = storm::umb::sparseModelFromUmb<ValueType>(umb1, importOptions);
105 assertEqualModel(model1);
106
107 // long round trip: model -> umb -> file -> umb -> model
108 storm::umb::toArchive(umb1, umbFile, exportOptions);
109 auto umb2 = storm::umb::importUmb(umbFile, importOptions);
110 ASSERT_TRUE(umb2.validate(validationErrors)) << validationErrors.str();
111 validationErrors.clear();
112 auto model2 = storm::umb::sparseModelFromUmb<ValueType>(umb2, importOptions);
113 assertEqualModel(model2);
114 removeUmbFile();
115 }
116
117 virtual void SetUp() override {
118#ifndef STORM_HAVE_Z3
119 GTEST_SKIP() << "Z3 not available.";
120#endif
121#ifndef STORM_HAVE_LIBARCHIVE
122 GTEST_SKIP() << "LibArchive not available.";
123#endif
124 }
125
126 virtual void TearDown() override {
127 removeUmbFile();
128 }
129};
130
131TEST_F(UmbRoundTripTest, brp_dtmc) {
133 run<double>(STORM_TEST_RESOURCES_DIR "/dtmc/brp-16-2.pm", "", options);
134 run<storm::RationalNumber>(STORM_TEST_RESOURCES_DIR "/dtmc/brp-16-2.pm", "", options);
135 run<storm::Interval>(STORM_TEST_RESOURCES_DIR "/dtmc/brp-16-2.pm", "", options);
136 run<storm::RationalInterval>(STORM_TEST_RESOURCES_DIR "/dtmc/brp-16-2.pm", "", options);
138 run<double>(STORM_TEST_RESOURCES_DIR "/dtmc/brp-16-2.pm", "", options);
140 run<double>(STORM_TEST_RESOURCES_DIR "/dtmc/brp-16-2.pm", "", options);
142 run<double>(STORM_TEST_RESOURCES_DIR "/dtmc/brp-16-2.pm", "", options);
143}
144
145TEST_F(UmbRoundTripTest, embedded_ctmc) {
147 run<double>(STORM_TEST_RESOURCES_DIR "/ctmc/embedded2.sm", "", options);
148 run<storm::RationalNumber>(STORM_TEST_RESOURCES_DIR "/ma/polling.ma", "N=3,Q=3", options);
149 run<storm::Interval>(STORM_TEST_RESOURCES_DIR "/ma/polling.ma", "N=3,Q=3", options);
150 run<storm::RationalInterval>(STORM_TEST_RESOURCES_DIR "/ma/polling.ma", "N=3,Q=3", options);
151}
152
153TEST_F(UmbRoundTripTest, firewire_mdp) {
155 run<double>(STORM_TEST_RESOURCES_DIR "/mdp/firewire3-0.5.nm", "", options);
156 run<storm::RationalNumber>(STORM_TEST_RESOURCES_DIR "/mdp/firewire3-0.5.nm", "", options);
157 run<storm::Interval>(STORM_TEST_RESOURCES_DIR "/mdp/firewire3-0.5.nm", "", options);
158 run<storm::RationalInterval>(STORM_TEST_RESOURCES_DIR "/mdp/firewire3-0.5.nm", "", options);
159 options.allowChoiceOriginsAsActions = true;
160 options.allowChoiceLabelingAsActions = false;
161 run<double>(STORM_TEST_RESOURCES_DIR "/mdp/firewire3-0.5.nm", "", options);
162}
163
164TEST_F(UmbRoundTripTest, polling_ma) {
166 run<double>(STORM_TEST_RESOURCES_DIR "/ma/polling.ma", "N=3,Q=3", options);
167 run<storm::RationalNumber>(STORM_TEST_RESOURCES_DIR "/ma/polling.ma", "N=3,Q=3", options);
168 run<storm::Interval>(STORM_TEST_RESOURCES_DIR "/ma/polling.ma", "N=3,Q=3", options);
169 run<storm::RationalInterval>(STORM_TEST_RESOURCES_DIR "/ma/polling.ma", "N=3,Q=3", options);
170}
171
172TEST_F(UmbRoundTripTest, robot_imdp) {
174 run<storm::Interval>(STORM_TEST_RESOURCES_DIR "/imdp/robot.prism", "delta=0.5", options);
175 run<storm::RationalInterval>(STORM_TEST_RESOURCES_DIR "/imdp/robot.prism", "delta=0.5", options);
176}
177
178TEST_F(UmbRoundTripTest, maze_pomdp) {
180 run<double>(STORM_TEST_RESOURCES_DIR "/pomdp/maze2.prism", "sl=0.5", options);
181 run<storm::RationalNumber>(STORM_TEST_RESOURCES_DIR "/pomdp/maze2.prism", "sl=0.5", options);
182 run<storm::Interval>(STORM_TEST_RESOURCES_DIR "/pomdp/maze2.prism", "sl=0.5", options);
183 run<storm::RationalInterval>(STORM_TEST_RESOURCES_DIR "/pomdp/maze2.prism", "sl=0.5", options);
184}
185
186} // namespace
187
188TEST(UmbTest, RationalEncoding) {
190 auto const int64max = storm::utility::convertNumber<storm::RationalNumber, int64_t>(std::numeric_limits<int64_t>::max());
191 auto const int64min = storm::utility::convertNumber<storm::RationalNumber, int64_t>(std::numeric_limits<int64_t>::min());
192 auto const uint64max = storm::utility::convertNumber<storm::RationalNumber, uint64_t>(std::numeric_limits<uint64_t>::max());
193
194 std::vector<storm::RationalNumber> values(17);
195 // The first 8 values are chosen such that they can be represented with two 64-bit numbers.
198 values[2] = one;
199 values[3] = -one;
202 values[6] = int64max / uint64max;
203 values[7] = int64min / uint64max;
204
205 auto const simpleRationals = std::span<storm::RationalNumber>(values.data(), 8);
206 ASSERT_EQ(128ull, storm::umb::ValueEncoding::getMinimalRationalSize(simpleRationals, false));
207 ASSERT_EQ(128ull, storm::umb::ValueEncoding::getMinimalRationalSize(simpleRationals, true));
208 auto encoded1 = storm::umb::ValueEncoding::createUint64FromRationalRange(simpleRationals, 128ull);
209 auto decoded1 = storm::umb::ValueEncoding::uint64ToRationalRangeView(encoded1, 128ull);
210 ASSERT_EQ(simpleRationals.size(), decoded1.size());
211 for (size_t i = 0; i < simpleRationals.size(); ++i) {
212 EXPECT_EQ(simpleRationals[i], decoded1[i]) << " at index " << i;
213 }
214
215 // The following values are chosen such that they are not representable with two 64-bit numbers.
216 values[8] = int64max + one;
217 values[9] = one / (uint64max + one);
218 values[10] = int64min - one;
219 values[11] = one / (int64min - one);
220 values[12] = (int64min - one) / (uint64max + one);
222 "949667607787274453086419753000949667607787274453086419753000949667607787274453086419753000949667607787274453086419753000949667607787274453086419753000"
223 "9496676077872744530864197530009496676077872744530864197530009496676077872744530864197530/"
224 "780116505469339517040847240228739241622101546262265311616467711470010820006007800398204693387501962318501358930877102188539546463329577703105788853954"
225 "134811616465520508472358467546262155762385699576193087775947700108638258539546825539241654967");
226 values[14] = one / values[13];
227 values[15] = -values[13];
228 values[16] = -values[14];
229
230 ASSERT_EQ(1616ull, storm::umb::ValueEncoding::getMinimalRationalSize(values, false));
231 ASSERT_EQ(1664ull, storm::umb::ValueEncoding::getMinimalRationalSize(values, true));
232 auto encoded2 = storm::umb::ValueEncoding::createUint64FromRationalRange(values, 1664ull);
233 auto decoded2 = storm::umb::ValueEncoding::uint64ToRationalRangeView(encoded2, 1664ull);
234
235 ASSERT_EQ(values.size(), decoded2.size());
236 for (size_t i = 0; i < values.size(); ++i) {
237 EXPECT_EQ(values[i], decoded2[i]) << " at index " << i;
238 }
239}
TEST_F(AssumptionCheckerTest, Brp_no_bisimulation)
TEST(UmbTest, RationalEncoding)
Definition UmbTest.cpp:188
BuilderOptions & setBuildAllLabels(bool newValue=true)
Should all reward models be built?
BuilderOptions & setBuildChoiceLabels(bool newValue=true)
Should the choice labels be built?
BuilderOptions & setBuildAllRewardModels(bool newValue=true)
Should all reward models be built?
BuilderOptions & setBuildChoiceOrigins(bool newValue=true)
Should the origins the different choices be built?
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.
static uint64_t getMinimalRationalSize(InputRange &&input, bool multiplesOf64)
static std::vector< uint64_t > createUint64FromRationalRange(InputRange &&input, uint64_t const numberSize)
static auto uint64ToRationalRangeView(InputRange &&input, uint64_t const numberSize)
storm::builder::BuilderOptions NextStateGeneratorOptions
storm::umb::UmbModel importUmb(std::filesystem::path const &umbLocation, ImportOptions const &options)
void toArchive(storm::umb::UmbModel const &umbModel, std::filesystem::path const &archivePath, ExportOptions const &options)
std::shared_ptr< storm::models::sparse::Model< ValueType > > sparseModelFromUmb(storm::umb::UmbModel const &umbModel, ImportOptions const &options)
Constructs a sparse model from the given UMB model.
storm::umb::UmbModel sparseModelToUmb(storm::models::sparse::Model< ValueType > const &model, ExportOptions const &options)
storm::prism::Program preprocess(storm::prism::Program const &program, std::map< storm::expressions::Variable, storm::expressions::Expression > const &constantDefinitions)
Definition prism.cpp:13
ValueType zero()
Definition constants.cpp:24
ValueType one()
Definition constants.cpp:19
TargetType convertNumber(SourceType const &number)
bool allowChoiceLabelingAsActions
Whether export of choice origins is enabled.
bool allowChoiceOriginsAsActions
Whether export of choice origins is enabled.
storm::io::CompressionMode compression
The type of compression used for the exported UMB model.
bool buildChoiceLabeling
Controls building of choice labelings.