Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
MarkovAutomatonSparseTransitionParser.cpp
Go to the documentation of this file.
2
9
10namespace storm {
11namespace parser {
12
13using namespace storm::utility::cstring;
14
15template<typename ValueType>
16typename MarkovAutomatonSparseTransitionParser<ValueType>::FirstPassResult MarkovAutomatonSparseTransitionParser<ValueType>::firstPass(
17 char const* buf, ExplicitModelParserOptions const& options) {
19
20 bool fixDeadlocks = options.fixDeadlocks;
21
22 // Skip the format hint if it is there.
23 buf = trimWhitespaces(buf);
24 if (buf[0] < '0' || buf[0] > '9') {
25 buf = forwardToLineEnd(buf);
26 buf = trimWhitespaces(buf);
27 }
28
29 // Now read the transitions.
30 uint_fast64_t source, target = 0;
31 uint_fast64_t lastsource = 0;
32 bool encounteredEOF = false;
33 bool stateHasMarkovianChoice = false;
34 bool stateHasProbabilisticChoice = false;
35 while (buf[0] != '\0' && !encounteredEOF) {
36 // At the current point, the next thing to read is the source state of the next choice to come.
37 source = checked_strtol(buf, &buf);
38
39 // Check if we encountered a state index that is bigger than all previously seen ones and record it if necessary.
40 if (source > result.highestStateIndex) {
41 result.highestStateIndex = source;
42 }
43
44 // If we have skipped some states, we need to reserve the space for the self-loop insertion in the second pass.
45 if (source > lastsource + 1) {
46 if (fixDeadlocks) {
47 result.numberOfNonzeroEntries += source - lastsource - 1;
48 result.numberOfChoices += source - lastsource - 1;
49 } else {
50 STORM_LOG_THROW(false, storm::exceptions::WrongFormatException,
51 "Found deadlock states (e.g. " << lastsource + 1 << ") during parsing. Please fix them or set the appropriate flag.");
52 }
53 } else if (source < lastsource) {
54 STORM_LOG_THROW(false, storm::exceptions::WrongFormatException,
55 "Illegal state choice order. A choice of state " << source << " appears at an illegal position.");
56 }
57
58 ++result.numberOfChoices;
59
60 // If we have moved to the next state, we need to clear the flag that stores whether or not the source has a Markovian or probabilistic choice.
61 if (source != lastsource) {
62 stateHasMarkovianChoice = false;
63 stateHasProbabilisticChoice = false;
64 }
65
66 // Record that the current source was the last source.
67 lastsource = source;
68
69 buf = trimWhitespaces(buf);
70
71 // Depending on the action name, the choice is either a probabilitic one or a markovian one.
72 bool isMarkovianChoice = false;
73 if (buf[0] == '!' && skipWord(buf) - buf == 1) {
74 isMarkovianChoice = true;
75 } else {
76 isMarkovianChoice = false;
77 }
78 buf = skipWord(buf);
79
80 if (isMarkovianChoice) {
81 STORM_LOG_THROW(!stateHasMarkovianChoice, storm::exceptions::WrongFormatException, "The state " << source << " has multiple Markovian choices.");
82 if (stateHasProbabilisticChoice) {
84 false, storm::exceptions::WrongFormatException,
85 "The state " << source
86 << " has a probabilistic choice preceding a Markovian choice. The Markovian choice must be the first choice listed.");
87 }
88 stateHasMarkovianChoice = true;
89 } else {
90 stateHasProbabilisticChoice = true;
91 }
92
93 // Go to the next line where the transitions start.
94 buf = forwardToNextLine(buf);
95
96 // Now that we have the source state and the information whether or not the current choice is probabilistic or Markovian, we need to read the list of
97 // successors and the probabilities/rates.
98 bool hasSuccessorState = false;
99 bool encounteredNewDistribution = false;
100 uint_fast64_t lastSuccessorState = 0;
101
102 // At this point, we need to check whether there is an additional successor or we have reached the next choice for the same or a different state.
103 do {
104 buf = trimWhitespaces(buf);
105 // If the end of the file was reached, we need to abort and check whether we are in a legal state.
106 if (buf[0] == '\0') {
107 if (!hasSuccessorState) {
108 STORM_LOG_THROW(false, storm::exceptions::WrongFormatException,
109 "Premature end-of-file. Expected at least one successor state for state " << source << ".");
110 } else {
111 // If there was at least one successor for the current choice, this is legal and we need to move on.
112 encounteredEOF = true;
113 }
114 } else if (buf[0] == '*') {
115 // As we have encountered a "*", we know that there is an additional successor state for the current choice.
116 buf = skipWord(buf);
117
118 // Now we need to read the successor state and check if we already saw a higher state index.
119 target = checked_strtol(buf, &buf);
120 if (target > result.highestStateIndex) {
121 result.highestStateIndex = target;
122 }
123 STORM_LOG_THROW(!hasSuccessorState || target > lastSuccessorState, storm::exceptions::WrongFormatException,
124 "Illegal transition order for source state " << source << ".");
125
126 // And the corresponding probability/rate.
127 double val = checked_strtod(buf, &buf);
128 STORM_LOG_THROW(val >= 0.0, storm::exceptions::WrongFormatException,
129 "Illegal negative probability/rate value for transition from " << source << " to " << target << ": " << val << ".");
130 STORM_LOG_THROW(isMarkovianChoice || val <= 1.0, storm::exceptions::WrongFormatException,
131 "Illegal probability value for transition from " << source << " to " << target << ": " << val << ".");
132
133 // We need to record that we found at least one successor state for the current choice.
134 hasSuccessorState = true;
135 lastSuccessorState = target;
136
137 // As we found a new successor, we need to increase the number of nonzero entries.
138 ++result.numberOfNonzeroEntries;
139
140 buf = forwardToNextLine(buf);
141 } else {
142 // If it was not a "*", we have to assume that we encountered the beginning of a new choice definition. In this case, we don't move the pointer
143 // to the buffer, because we still need to read the new source state.
144 encounteredNewDistribution = true;
145 }
146 } while (!encounteredEOF && !encounteredNewDistribution);
147 }
148
149 // If there are some states with indices that are behind the last source for which no transition was specified,
150 // we need to reserve some space for introducing self-loops later.
151 if (fixDeadlocks) {
152 result.numberOfNonzeroEntries += result.highestStateIndex - lastsource;
153 result.numberOfChoices += result.highestStateIndex - lastsource;
154 } else {
155 STORM_LOG_THROW(false, storm::exceptions::WrongFormatException,
156 "Found deadlock states (e.g. " << lastsource + 1 << ") during parsing. Please fix them or set the appropriate flag.");
157 }
158
159 return result;
160}
161
162template<typename ValueType>
163typename MarkovAutomatonSparseTransitionParser<ValueType>::Result MarkovAutomatonSparseTransitionParser<ValueType>::secondPass(
164 char const* buf, FirstPassResult const& firstPassResult, ExplicitModelParserOptions const& options) {
165 Result result(firstPassResult);
166
167 bool fixDeadlocks = options.fixDeadlocks;
168
169 // Skip the format hint if it is there.
170 buf = trimWhitespaces(buf);
171 if (buf[0] < '0' || buf[0] > '9') {
172 buf = forwardToLineEnd(buf);
173 buf = trimWhitespaces(buf);
174 }
175
176 // Now read the transitions.
177 uint_fast64_t source, target = 0;
178 uint_fast64_t lastsource = 0;
179 bool encounteredEOF = false;
180 uint_fast64_t currentChoice = 0;
181
182 // The first choice of the first state already starts a new row group of the matrix.
183 result.transitionMatrixBuilder.newRowGroup(0);
184
185 while (buf[0] != '\0' && !encounteredEOF) {
186 // At the current point, the next thing to read is the source state of the next choice to come.
187 source = checked_strtol(buf, &buf);
188
189 // If we have skipped some states, we need to insert self-loops if requested.
190 if (source > lastsource + 1) {
191 if (fixDeadlocks) {
192 for (uint_fast64_t index = lastsource + 1; index < source; ++index) {
193 result.transitionMatrixBuilder.newRowGroup(currentChoice);
194 result.transitionMatrixBuilder.addNextValue(currentChoice, index, 1);
195 ++currentChoice;
196 }
197 } else {
198 STORM_LOG_THROW(false, storm::exceptions::WrongFormatException,
199 "Found deadlock states (e.g. " << lastsource + 1 << ") during parsing. Please fix them or set the appropriate flag.");
200 }
201 }
202
203 if (source != lastsource) {
204 // If we skipped to a new state we need to create a new row group for the choices of the new state.
205 result.transitionMatrixBuilder.newRowGroup(currentChoice);
206 }
207
208 // Record that the current source was the last source.
209 lastsource = source;
210
211 buf = trimWhitespaces(buf);
212
213 // Depending on the action name, the choice is either a probabilitic one or a markovian one.
214 bool isMarkovianChoice = false;
215 if (buf[0] == '!' && skipWord(buf) - buf == 1) {
216 isMarkovianChoice = true;
217
218 // Mark the current state as a Markovian one.
219 result.markovianStates.set(source, true);
220 } else {
221 isMarkovianChoice = false;
222 }
223
224 // Go to the next line where the transitions start.
225 buf = forwardToNextLine(buf);
226
227 // Now that we have the source state and the information whether or not the current choice is probabilistic or Markovian, we need to read the list of
228 // successors and the probabilities/rates.
229 bool encounteredNewDistribution = false;
230
231 // At this point, we need to check whether there is an additional successor or we have reached the next choice for the same or a different state.
232 do {
233 buf = trimWhitespaces(buf);
234
235 // If the end of the file was reached, we need to abort and check whether we are in a legal state.
236 if (buf[0] == '\0') {
237 // Under the assumption that the currently open choice has at least one successor (which is given after the first run)
238 // we may legally stop reading here.
239 encounteredEOF = true;
240 } else if (buf[0] == '*') {
241 // As we have encountered a "*", we know that there is an additional successor state for the current choice.
242 buf = skipWord(buf);
243
244 // Now we need to read the successor state and check if we already saw a higher state index.
245 target = checked_strtol(buf, &buf);
246
247 // And the corresponding probability/rate.
248 double val = checked_strtod(buf, &buf);
249
250 // Record the value as well as the exit rate in case of a Markovian choice.
251 result.transitionMatrixBuilder.addNextValue(currentChoice, target, val);
252 if (isMarkovianChoice) {
253 result.exitRates[source] += val;
254 }
255
256 buf = forwardToNextLine(buf);
257 } else {
258 // If it was not a "*", we have to assume that we encountered the beginning of a new choice definition. In this case, we don't move the pointer
259 // to the buffer, because we still need to read the new source state.
260 encounteredNewDistribution = true;
261 }
262 } while (!encounteredEOF && !encounteredNewDistribution);
263
264 ++currentChoice;
265 }
266
267 // If there are some states with indices that are behind the last source for which no transition was specified,
268 // we need to insert the self-loops now. Note that we assume all these states to be Markovian.
269 if (fixDeadlocks) {
270 for (uint_fast64_t index = lastsource + 1; index <= firstPassResult.highestStateIndex; ++index) {
271 result.markovianStates.set(index, true);
272 result.exitRates[index] = storm::utility::one<ValueType>();
273 result.transitionMatrixBuilder.newRowGroup(currentChoice);
274 result.transitionMatrixBuilder.addNextValue(currentChoice, index, storm::utility::one<ValueType>());
275 ++currentChoice;
276 }
277 } else {
278 STORM_LOG_THROW(false, storm::exceptions::WrongFormatException,
279 "Found deadlock states (e.g. " << lastsource + 1 << ") during parsing. Please fix them or set the appropriate flag.");
280 }
281
282 return result;
283}
284
285template<typename ValueType>
287 std::string const& filename, ExplicitModelParserOptions const& options) {
288 // Set the locale to correctly recognize floating point numbers.
289 setlocale(LC_NUMERIC, "C");
290
291 // Open file and prepare pointer to buffer.
292 MappedFile file(filename.c_str());
293 char const* buf = file.getData();
294
295 return secondPass(buf, firstPass(buf, options), options);
296}
297
299
300} // namespace parser
301} // namespace storm
Opens a file and maps it to memory providing a char* containing the file content.
Definition MappedFile.h:21
char const * getData() const
Returns a pointer to the beginning of the mapped file data.
A class providing the functionality to parse the transitions of a Markov automaton.
static Result parseMarkovAutomatonTransitions(std::string const &filename, ExplicitModelParserOptions const &options=ExplicitModelParserOptions())
Parses the given file under the assumption that it contains a Markov automaton specified in the appro...
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:28
Contains all file parsers and helper classes.
char const * forwardToLineEnd(char const *buffer)
Encapsulates the usage of function @strcspn to forward to the end of the line (next char is the newli...
Definition cstring.cpp:72
char const * skipWord(char const *buf)
Skips all numbers, letters and special characters.
Definition cstring.cpp:49
double checked_strtod(char const *str, char const **end)
Calls strtod() internally and checks if the new pointer is different from the original one,...
Definition cstring.cpp:36
uint_fast64_t checked_strtol(char const *str, char const **end)
Calls strtol() internally and checks if the new pointer is different from the original one,...
Definition cstring.cpp:21
char const * forwardToNextLine(char const *buffer)
Encapsulates the usage of function @strchr to forward to the next line.
Definition cstring.cpp:79
char const * trimWhitespaces(char const *buf)
Skips spaces, tabs, newlines and carriage returns.
Definition cstring.cpp:62
ValueType one()
Definition constants.cpp:19
A structure representing the result of the first pass of this parser.