Storm 1.11.1.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
DirectEncodingParser.cpp
Go to the documentation of this file.
2
3#include <boost/algorithm/string.hpp>
4#include <boost/algorithm/string/predicate.hpp>
5#include <iostream>
6#include <regex>
7#include <string>
8
16#include "storm/io/file.h"
21
22namespace storm {
23namespace parser {
24
25template<typename ValueType, typename RewardModelType>
26std::shared_ptr<storm::models::sparse::Model<ValueType, RewardModelType>> DirectEncodingParser<ValueType, RewardModelType>::parseModel(
27 std::string const& filename, DirectEncodingParserOptions const& options) {
28 // Load file
29 STORM_LOG_INFO("Reading from file " << filename);
30 std::ifstream file;
31 storm::io::openFile(filename, file);
32 std::string line;
33
34 // Initialize
35 ValueParser<ValueType> valueParser;
36 bool sawType = false;
37 bool sawParameters = false;
38 std::unordered_map<std::string, ValueType> placeholders;
39 size_t nrStates = 0;
40 size_t nrChoices = 0;
42 std::vector<std::string> rewardModelNames;
43 std::shared_ptr<storm::storage::sparse::ModelComponents<ValueType, RewardModelType>> modelComponents;
44
45 // Parse header
46 while (storm::io::getline(file, line)) {
47 if (line.empty() || boost::starts_with(line, "//")) {
48 continue;
49 }
50
51 if (boost::starts_with(line, "@type: ")) {
52 // Parse type
53 STORM_LOG_THROW(!sawType, storm::exceptions::WrongFormatException, "Type declared twice");
54 type = storm::models::getModelType(line.substr(7));
55 STORM_LOG_TRACE("Model type: " << type);
56 STORM_LOG_THROW(type != storm::models::ModelType::S2pg, storm::exceptions::NotSupportedException,
57 "Stochastic Two Player Games in DRN format are not supported.");
58 sawType = true;
59
60 } else if (line == "@parameters") {
61 // Parse parameters
62 STORM_LOG_THROW(!sawParameters, storm::exceptions::WrongFormatException, "Parameters declared twice");
63 storm::io::getline(file, line);
64 if (line != "") {
65 std::vector<std::string> parameters;
66 boost::split(parameters, line, boost::is_any_of(" "));
67 for (std::string const& parameter : parameters) {
68 STORM_LOG_TRACE("New parameter: " << parameter);
69 valueParser.addParameter(parameter);
70 }
71 }
72 sawParameters = true;
73
74 } else if (line == "@placeholders") {
75 // Parse placeholders
76 while (storm::io::getline(file, line)) {
77 size_t posColon = line.find(':');
78 STORM_LOG_THROW(posColon != std::string::npos, storm::exceptions::WrongFormatException, "':' not found.");
79 std::string placeName = line.substr(0, posColon - 1);
80 STORM_LOG_THROW(placeName.front() == '$', storm::exceptions::WrongFormatException, "Placeholder must start with dollar symbol $.");
81 std::string valueStr = line.substr(posColon + 2);
82 ValueType value = parseValue(valueStr, placeholders, valueParser);
83 STORM_LOG_TRACE("Placeholder " << placeName << " for value " << value);
84 auto ret = placeholders.insert(std::make_pair(placeName.substr(1), value));
85 STORM_LOG_THROW(ret.second, storm::exceptions::WrongFormatException, "Placeholder '$" << placeName << "' was already defined before.");
86 if (file.peek() == '@') {
87 // Next character is @ -> placeholder definitions ended
88 break;
89 }
90 }
91 } else if (line == "@reward_models") {
92 // Parse reward models
93 STORM_LOG_THROW(rewardModelNames.empty(), storm::exceptions::WrongFormatException, "Reward model names declared twice");
94 storm::io::getline(file, line);
95 boost::split(rewardModelNames, line, boost::is_any_of("\t "));
96 } else if (line == "@nr_states") {
97 // Parse no. of states
98 STORM_LOG_THROW(nrStates == 0, storm::exceptions::WrongFormatException, "Number states declared twice");
99 storm::io::getline(file, line);
100 nrStates = parseNumber<size_t>(line);
101 } else if (line == "@nr_choices") {
102 STORM_LOG_THROW(nrChoices == 0, storm::exceptions::WrongFormatException, "Number of actions declared twice");
103 storm::io::getline(file, line);
104 nrChoices = parseNumber<size_t>(line);
105 } else if (line == "@model") {
106 // Parse rest of the model
107 STORM_LOG_THROW(sawType, storm::exceptions::WrongFormatException, "Type has to be declared before model.");
108 STORM_LOG_THROW(nrStates != 0, storm::exceptions::WrongFormatException, "No. of states has to be declared before model.");
109 STORM_LOG_THROW(!options.buildChoiceLabeling || nrChoices != 0, storm::exceptions::WrongFormatException,
110 "No. of actions (@nr_choices) has to be declared before model.");
111 STORM_LOG_WARN_COND(nrChoices != 0, "No. of actions has to be declared. We may continue now, but future versions might not support this.");
112 // Construct model components
113 modelComponents = parseStates(file, type, nrStates, nrChoices, placeholders, valueParser, rewardModelNames, options);
114 break;
115 } else {
116 STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Could not parse line '" << line << "'.");
117 }
118 }
119 // Done parsing
121
122 // Build model
123 return storm::utility::builder::buildModelFromComponents(type, std::move(*modelComponents));
124}
125
126template<typename ValueType, typename RewardModelType>
127std::shared_ptr<storm::storage::sparse::ModelComponents<ValueType, RewardModelType>> DirectEncodingParser<ValueType, RewardModelType>::parseStates(
128 std::istream& file, storm::models::ModelType type, size_t stateSize, size_t nrChoices, std::unordered_map<std::string, ValueType> const& placeholders,
129 ValueParser<ValueType> const& valueParser, std::vector<std::string> const& rewardModelNames, DirectEncodingParserOptions const& options) {
130 // Initialize
131 auto modelComponents = std::make_shared<storm::storage::sparse::ModelComponents<ValueType, RewardModelType>>();
132 bool nonDeterministic =
134 bool continuousTime = (type == storm::models::ModelType::Ctmc || type == storm::models::ModelType::MarkovAutomaton);
136 modelComponents->stateLabeling = storm::models::sparse::StateLabeling(stateSize);
137 modelComponents->observabilityClasses = std::vector<uint32_t>();
138 modelComponents->observabilityClasses->resize(stateSize);
139 if (options.buildChoiceLabeling) {
140 modelComponents->choiceLabeling = storm::models::sparse::ChoiceLabeling(nrChoices);
141 }
142 std::vector<std::vector<ValueType>> stateRewards;
143 std::vector<std::vector<ValueType>> actionRewards;
144 if (continuousTime) {
145 modelComponents->exitRates = std::vector<ValueType>(stateSize);
147 modelComponents->markovianStates = storm::storage::BitVector(stateSize);
148 }
149 }
150 // We parse rates for continuous time models.
151 if (type == storm::models::ModelType::Ctmc) {
152 modelComponents->rateTransitions = true;
153 }
154
155 // Iterate over all lines
156 std::string line;
157 size_t row = 0;
158 size_t state = 0;
159 uint64_t lineNumber = 0;
160 bool firstState = true;
161 bool firstActionForState = true;
162 while (storm::io::getline(file, line)) {
163 lineNumber++;
164 if (boost::starts_with(line, "//")) {
165 continue;
166 }
167 if (line.empty()) {
168 continue;
169 }
170 STORM_LOG_TRACE("Parsing line no " << lineNumber << " : " << line);
171 boost::trim_left(line);
172 if (boost::starts_with(line, "state ")) {
173 // New state
174 if (firstState) {
175 firstState = false;
176 } else {
177 ++state;
178 ++row;
179 }
180 firstActionForState = true;
181 STORM_LOG_TRACE("New state " << state);
182 STORM_LOG_THROW(state <= stateSize, storm::exceptions::WrongFormatException, "More states detected than declared (in @nr_states).");
183
184 // Parse state id
185 line = line.substr(6); // Remove "state "
186 std::string curString = line;
187 size_t posEnd = line.find(" ");
188 if (posEnd != std::string::npos) {
189 curString = line.substr(0, posEnd);
190 line = line.substr(posEnd + 1);
191 } else {
192 line = "";
193 }
194 size_t parsedId = parseNumber<size_t>(curString);
195 STORM_LOG_THROW(state == parsedId, storm::exceptions::WrongFormatException,
196 "In line " << lineNumber << " state ids are not ordered and without gaps. Expected " << state << " but got " << parsedId << ".");
197 if (nonDeterministic) {
198 STORM_LOG_TRACE("new Row Group starts at " << row << ".");
199 builder.newRowGroup(row);
200 STORM_LOG_THROW(nrChoices == 0 || builder.getCurrentRowGroupCount() <= nrChoices, storm::exceptions::WrongFormatException,
201 "More actions detected than declared (in @nr_choices).");
202 }
203
204 if (continuousTime) {
205 // Parse exit rate for CTMC or MA
206 STORM_LOG_THROW(boost::starts_with(line, "!"), storm::exceptions::WrongFormatException, "Exit rate missing in " << lineNumber);
207 line = line.substr(1); // Remove "!"
208 curString = line;
209 posEnd = line.find(" ");
210 if (posEnd != std::string::npos) {
211 curString = line.substr(0, posEnd);
212 line = line.substr(posEnd + 1);
213 } else {
214 line = "";
215 }
216 ValueType exitRate = parseValue(curString, placeholders, valueParser);
217 if (type == storm::models::ModelType::MarkovAutomaton && !storm::utility::isZero<ValueType>(exitRate)) {
218 modelComponents->markovianStates.get().set(state);
219 }
220 STORM_LOG_TRACE("Exit rate " << exitRate);
221 modelComponents->exitRates.get()[state] = exitRate;
222 }
223
225 if (boost::starts_with(line, "{")) {
226 size_t posEndObservation = line.find("}");
227 std::string observation = line.substr(1, posEndObservation - 1);
228 STORM_LOG_TRACE("State observation " << observation);
229 modelComponents->observabilityClasses.value()[state] = std::stoi(observation);
230 line = line.substr(posEndObservation + 1);
231 if (!line.empty()) {
232 STORM_LOG_THROW(line.starts_with(" "), storm::exceptions::WrongFormatException,
233 "Expected whitespace after observation in line " << lineNumber);
234
235 line = line.substr(1);
236 }
237 } else {
238 STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Expected an observation for state " << state << " in line " << lineNumber);
239 }
240 }
241
242 if (boost::starts_with(line, "[")) {
243 // Parse rewards
244 size_t posEndReward = line.find(']');
245 STORM_LOG_THROW(posEndReward != std::string::npos, storm::exceptions::WrongFormatException, "] missing in line " << lineNumber << " .");
246 std::string rewardsStr = line.substr(1, posEndReward - 1);
247 STORM_LOG_TRACE("State rewards: " << rewardsStr);
248 std::vector<std::string> rewards;
249 boost::split(rewards, rewardsStr, boost::is_any_of(","));
250 if (stateRewards.size() < rewards.size()) {
251 stateRewards.resize(rewards.size());
252 }
253 auto stateRewardsIt = stateRewards.begin();
254 for (auto const& rew : rewards) {
255 auto rewardValue = parseValue(rew, placeholders, valueParser);
256 if (!storm::utility::isZero(rewardValue)) {
257 if (stateRewardsIt->empty()) {
258 stateRewardsIt->resize(stateSize, storm::utility::zero<ValueType>());
259 }
260 (*stateRewardsIt)[state] = std::move(rewardValue);
261 }
262 ++stateRewardsIt;
263 }
264 line = line.substr(posEndReward + 1);
265 }
266
267 // Parse labels
268 if (!line.empty()) {
269 std::vector<std::string> labels;
270 // Labels are separated by whitespace and can optionally be enclosed in quotation marks
271 // Regex for labels with two cases:
272 // * Enclosed in quotation marks: \"([^\"]+?)\"(?=(\s|$|\"))
273 // - First part matches string enclosed in quotation marks with no quotation mark inbetween (\"([^\"]+?)\")
274 // - second part is lookahead which ensures that after the matched part either whitespace, end of line or a new quotation mark follows
275 // (?=(\s|$|\"))
276 // * Separated by whitespace: [^\s\"]+?(?=(\s|$))
277 // - First part matches string without whitespace and quotation marks [^\s\"]+?
278 // - Second part is again lookahead matching whitespace or end of line (?=(\s|$))
279 std::regex labelRegex(R"(\"([^\"]+?)\"(?=(\s|$|\"))|([^\s\"]+?(?=(\s|$))))");
280
281 // Iterate over matches
282 auto match_begin = std::sregex_iterator(line.begin(), line.end(), labelRegex);
283 auto match_end = std::sregex_iterator();
284 for (std::sregex_iterator i = match_begin; i != match_end; ++i) {
285 std::smatch match = *i;
286 // Find matched group and add as label
287 if (match.length(1) > 0) {
288 labels.push_back(match.str(1));
289 } else {
290 labels.push_back(match.str(3));
291 }
292 }
293
294 for (std::string const& label : labels) {
295 if (!modelComponents->stateLabeling.containsLabel(label)) {
296 modelComponents->stateLabeling.addLabel(label);
297 }
298 modelComponents->stateLabeling.addLabelToState(label, state);
299 STORM_LOG_TRACE("New label: '" << label << "'");
300 }
301 }
302 } else if (boost::starts_with(line, "action ")) {
303 // New action
304 if (firstActionForState) {
305 firstActionForState = false;
306 } else {
307 ++row;
308 }
309 STORM_LOG_TRACE("New action: " << row);
310 line = line.substr(7);
311 std::string curString = line;
312 size_t posEnd = line.find(" ");
313 if (posEnd != std::string::npos) {
314 curString = line.substr(0, posEnd);
315 line = line.substr(posEnd + 1);
316 } else {
317 line = "";
318 }
319
320 // curString contains action name.
321 if (options.buildChoiceLabeling) {
322 if (curString != "__NOLABEL__") {
323 if (!modelComponents->choiceLabeling.value().containsLabel(curString)) {
324 modelComponents->choiceLabeling.value().addLabel(curString);
325 }
326 modelComponents->choiceLabeling.value().addLabelToChoice(curString, row);
327 }
328 }
329 // Check for rewards
330 if (boost::starts_with(line, "[")) {
331 // Rewards found
332 size_t posEndReward = line.find(']');
333 STORM_LOG_THROW(posEndReward != std::string::npos, storm::exceptions::WrongFormatException, "] missing.");
334 std::string rewardsStr = line.substr(1, posEndReward - 1);
335 STORM_LOG_TRACE("Action rewards: " << rewardsStr);
336 std::vector<std::string> rewards;
337 boost::split(rewards, rewardsStr, boost::is_any_of(","));
338 if (actionRewards.size() < rewards.size()) {
339 actionRewards.resize(rewards.size());
340 }
341 auto actionRewardsIt = actionRewards.begin();
342 for (auto const& rew : rewards) {
343 auto rewardValue = parseValue(rew, placeholders, valueParser);
344 if (!storm::utility::isZero(rewardValue)) {
345 if (actionRewardsIt->size() <= row) {
346 actionRewardsIt->resize(std::max(row + 1, stateSize), storm::utility::zero<ValueType>());
347 }
348 (*actionRewardsIt)[row] = std::move(rewardValue);
349 }
350 ++actionRewardsIt;
351 }
352 line = line.substr(posEndReward + 1);
353 }
354
355 } else {
356 // New transition
357 size_t posColon = line.find(':');
358 STORM_LOG_THROW(posColon != std::string::npos, storm::exceptions::WrongFormatException,
359 "':' not found in '" << line << "' on line " << lineNumber << ".");
360 size_t target = parseNumber<size_t>(line.substr(0, posColon - 1));
361 std::string valueStr = line.substr(posColon + 2);
362 ValueType value = parseValue(valueStr, placeholders, valueParser);
363 STORM_LOG_TRACE("Transition " << row << " -> " << target << ": " << value);
364 STORM_LOG_THROW(target < stateSize, storm::exceptions::WrongFormatException,
365 "In line " << lineNumber << " target state " << target << " is greater than state size " << stateSize);
366 builder.addNextValue(row, target, value);
367 }
368
370 std::cout << "Parsed " << state << "/" << stateSize << " states before abort.\n";
371 STORM_LOG_THROW(false, storm::exceptions::AbortException, "Aborted in state space exploration.");
372 break;
373 }
374
375 } // end state iteration
376 STORM_LOG_TRACE("Finished parsing");
377
378 if (nonDeterministic) {
379 STORM_LOG_THROW(nrChoices == 0 || builder.getLastRow() + 1 == nrChoices, storm::exceptions::WrongFormatException,
380 "Number of actions detected (at least " << builder.getLastRow() + 1 << ") does not match number of actions declared (" << nrChoices
381 << ", in @nr_choices).");
382 }
383
384 // Build transition matrix
385 modelComponents->transitionMatrix = builder.build(row + 1, stateSize, nonDeterministic ? stateSize : 0);
386 STORM_LOG_TRACE("Built matrix");
387
388 // Build reward models
389 uint64_t numRewardModels = std::max(stateRewards.size(), actionRewards.size());
390 for (uint64_t i = 0; i < numRewardModels; ++i) {
391 std::string rewardModelName;
392 if (rewardModelNames.size() <= i) {
393 rewardModelName = "rew" + std::to_string(i);
394 } else {
395 rewardModelName = rewardModelNames[i];
396 }
397 std::optional<std::vector<ValueType>> stateRewardVector, actionRewardVector;
398 if (i < stateRewards.size() && !stateRewards[i].empty()) {
399 stateRewardVector = std::move(stateRewards[i]);
400 }
401 if (i < actionRewards.size() && !actionRewards[i].empty()) {
402 actionRewards[i].resize(row + 1, storm::utility::zero<ValueType>());
403 actionRewardVector = std::move(actionRewards[i]);
404 }
405 modelComponents->rewardModels.emplace(
406 rewardModelName, storm::models::sparse::StandardRewardModel<ValueType>(std::move(stateRewardVector), std::move(actionRewardVector)));
407 }
408 STORM_LOG_TRACE("Built reward models");
409 return modelComponents;
410}
411
412template<typename ValueType, typename RewardModelType>
413ValueType DirectEncodingParser<ValueType, RewardModelType>::parseValue(std::string const& valueStr,
414 std::unordered_map<std::string, ValueType> const& placeholders,
415 ValueParser<ValueType> const& valueParser) {
416 if (boost::starts_with(valueStr, "$")) {
417 auto it = placeholders.find(valueStr.substr(1));
418 STORM_LOG_THROW(it != placeholders.end(), storm::exceptions::WrongFormatException, "Placeholder " << valueStr << " unknown.");
419 return it->second;
420 } else {
421 // Use default value parser
422 return valueParser.parseValue(valueStr);
423 }
424}
425
426// Template instantiations.
427template class DirectEncodingParser<double>;
428template class DirectEncodingParser<storm::RationalNumber>;
429template class DirectEncodingParser<storm::RationalFunction>;
430template class DirectEncodingParser<storm::Interval>;
431
432} // namespace parser
433} // namespace storm
This class manages the labeling of the choice space with a number of (atomic) labels.
This class manages the labeling of the state space with a number of (atomic) labels.
Parser for models in the DRN format with explicit encoding.
static std::shared_ptr< storm::models::sparse::Model< ValueType, RewardModelType > > parseModel(std::string const &fil, DirectEncodingParserOptions const &options=DirectEncodingParserOptions())
Load a model in DRN format from a file and create the model.
Parser for values according to their ValueType.
Definition ValueParser.h:23
void addParameter(std::string const &parameter)
Add declaration of parameter.
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.
index_type getCurrentRowGroupCount() const
Retrieves the current row group count.
index_type getLastRow() const
Retrieves the most recently used row.
void addNextValue(index_type row, index_type column, value_type const &value)
Sets the matrix entry at the given row and column to the given value.
void newRowGroup(index_type startingRow)
Starts a new row group in the matrix.
SparseMatrix< value_type > build(index_type overriddenRowCount=0, index_type overriddenColumnCount=0, index_type overriddenRowGroupCount=0)
#define STORM_LOG_INFO(message)
Definition logging.h:24
#define STORM_LOG_TRACE(message)
Definition logging.h:12
#define STORM_LOG_WARN_COND(cond, message)
Definition macros.h:38
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
SFTBDDChecker::ValueType ValueType
std::basic_istream< CharT, Traits > & getline(std::basic_istream< CharT, Traits > &input, std::basic_string< CharT, Traits, Allocator > &str)
Overloaded getline function which handles different types of newline ( and \r).
Definition file.h:80
void closeFile(std::ofstream &stream)
Close the given file after writing.
Definition file.h:47
void openFile(std::string const &filepath, std::ofstream &filestream, bool append=false, bool silent=false)
Open the given file for writing.
Definition file.h:18
ModelType getModelType(std::string const &type)
Definition ModelType.cpp:9
std::shared_ptr< storm::models::sparse::Model< ValueType, RewardModelType > > buildModelFromComponents(storm::models::ModelType modelType, storm::storage::sparse::ModelComponents< ValueType, RewardModelType > &&components)
Definition builder.cpp:20
bool isTerminate()
Check whether the program should terminate (due to some abort signal).
bool isZero(ValueType const &a)
Definition constants.cpp:39