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