Storm 1.11.1.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
NondeterministicSparseTransitionParser.cpp
Go to the documentation of this file.
2
3#include <string>
4
14
15namespace storm {
16namespace parser {
17
18using namespace storm::utility::cstring;
19
20template<typename ValueType>
23 return NondeterministicSparseTransitionParser::parse(filename, false, emptyMatrix);
24}
25
26template<typename ValueType>
27template<typename MatrixValueType>
29 std::string const& filename, storm::storage::SparseMatrix<MatrixValueType> const& modelInformation) {
30 return NondeterministicSparseTransitionParser::parse(filename, true, modelInformation);
31}
32
33template<typename ValueType>
34template<typename MatrixValueType>
36 std::string const& filename, bool isRewardFile, storm::storage::SparseMatrix<MatrixValueType> const& modelInformation) {
37 // Enforce locale where decimal point is '.'.
38 setlocale(LC_NUMERIC, "C");
39
40 // Open file.
41 MappedFile file(filename.c_str());
42 char const* buf = file.getData();
43
44 // Perform first pass, i.e. obtain number of columns, rows and non-zero elements.
46 NondeterministicSparseTransitionParser::firstPass(file.getData(), isRewardFile, modelInformation);
47
48 // If first pass returned zero, the file format was wrong.
49 if (firstPass.numberOfNonzeroEntries == 0) {
50 STORM_LOG_ERROR("Error while parsing " << filename << ": erroneous file format.");
51 throw storm::exceptions::WrongFormatException() << "Error while parsing " << filename << ": erroneous file format.";
52 }
53
54 // Perform second pass.
55
56 // Skip the format hint if it is there.
57 buf = trimWhitespaces(buf);
58 if (buf[0] < '0' || buf[0] > '9') {
59 buf = forwardToLineEnd(buf);
60 buf = trimWhitespaces(buf);
61 }
62
63 if (isRewardFile) {
64 // The reward matrix should match the size of the transition matrix.
65 if (firstPass.choices > modelInformation.getRowCount() || (uint_fast64_t)(firstPass.highestStateIndex + 1) > modelInformation.getColumnCount()) {
66 STORM_LOG_ERROR("Reward matrix size exceeds transition matrix size.");
67 throw storm::exceptions::OutOfRangeException() << "Reward matrix size exceeds transition matrix size.";
68 } else if (firstPass.choices != modelInformation.getRowCount()) {
69 STORM_LOG_ERROR("Reward matrix row count does not match transition matrix row count.");
70 throw storm::exceptions::OutOfRangeException() << "Reward matrix row count does not match transition matrix row count.";
71 } else if (firstPass.numberOfNonzeroEntries > modelInformation.getEntryCount()) {
72 STORM_LOG_ERROR("The reward matrix has more entries than the transition matrix. There must be a reward for a non existent transition");
73 throw storm::exceptions::OutOfRangeException() << "The reward matrix has more entries than the transition matrix.";
74 } else {
75 firstPass.highestStateIndex = modelInformation.getColumnCount() - 1;
76 }
77 }
78
79 // Create the matrix builder.
80 // The matrix to be build should have as many columns as we have nodes and as many rows as we have choices.
81 // Those two values, as well as the number of nonzero elements, was been calculated in the first run.
82 STORM_LOG_INFO("Attempting to create matrix of size " << firstPass.choices << " x " << (firstPass.highestStateIndex + 1) << " with "
83 << firstPass.numberOfNonzeroEntries << " entries.");
85 if (!isRewardFile) {
87 true, true, firstPass.highestStateIndex + 1);
88 } else {
90 true, true, modelInformation.getRowGroupCount());
91 }
92
93 // Initialize variables for the parsing run.
94 uint_fast64_t source = 0, target = 0, lastSource = 0, choice = 0, lastChoice = 0, curRow = 0;
95 double val = 0.0;
96 bool dontFixDeadlocks = storm::settings::getModule<storm::settings::modules::BuildSettings>().isDontFixDeadlocksSet();
97 bool hadDeadlocks = false;
98
99 // The first state already starts a new row group of the matrix.
100 matrixBuilder.newRowGroup(0);
101
102 // Read all transitions from file.
103 while (buf[0] != '\0') {
104 // Read source state and choice.
105 source = checked_strtol(buf, &buf);
106 choice = checked_strtol(buf, &buf);
107
108 if (isRewardFile) {
109 // If we have switched the source state, we possibly need to insert the rows of the last
110 // source state.
111 if (source != lastSource) {
112 curRow += ((modelInformation.getRowGroupIndices())[lastSource + 1] - (modelInformation.getRowGroupIndices())[lastSource]) - (lastChoice + 1);
113 }
114
115 // If we skipped some states, we need to reserve empty rows for all their nondeterministic
116 // choices and create the row groups.
117 for (uint_fast64_t i = lastSource + 1; i < source; ++i) {
118 matrixBuilder.newRowGroup(modelInformation.getRowGroupIndices()[i]);
119 curRow += ((modelInformation.getRowGroupIndices())[i + 1] - (modelInformation.getRowGroupIndices())[i]);
120 }
121
122 // If we moved to the next source, we need to open the next row group.
123 if (source != lastSource) {
124 matrixBuilder.newRowGroup(modelInformation.getRowGroupIndices()[source]);
125 }
126
127 // If we advanced to the next state, but skipped some choices, we have to reserve rows
128 // for them
129 if (source != lastSource) {
130 curRow += choice + 1;
131 } else if (choice != lastChoice) {
132 curRow += choice - lastChoice;
133 }
134 } else {
135 // Increase line count if we have either finished reading the transitions of a certain state
136 // or we have finished reading one nondeterministic choice of a state.
137 if ((source != lastSource || choice != lastChoice)) {
138 ++curRow;
139 }
140
141 // Check if we have skipped any source node, i.e. if any node has no
142 // outgoing transitions. If so, insert a self-loop.
143 // Also begin a new rowGroup for the skipped state.
144 for (uint_fast64_t node = lastSource + 1; node < source; node++) {
145 hadDeadlocks = true;
146 if (!dontFixDeadlocks) {
147 matrixBuilder.newRowGroup(curRow);
148 matrixBuilder.addNextValue(curRow, node, 1);
149 ++curRow;
150 STORM_LOG_INFO("Warning while parsing " << filename << ": node " << node << " has no outgoing transitions. A self-loop was inserted.");
151 } else {
152 STORM_LOG_ERROR("Error while parsing " << filename << ": node " << node << " has no outgoing transitions.");
153 }
154 }
155 if (source != lastSource) {
156 // Create a new rowGroup for the source, if this is the first choice we encounter for this state.
157 matrixBuilder.newRowGroup(curRow);
158 }
159 }
160
161 // Read target and value and write it to the matrix.
162 target = checked_strtol(buf, &buf);
163 val = checked_strtod(buf, &buf);
164 matrixBuilder.addNextValue(curRow, target, val);
165
166 lastSource = source;
167 lastChoice = choice;
168
169 // Proceed to beginning of next line in file and next row in matrix.
170 buf = forwardToLineEnd(buf);
171
172 buf = trimWhitespaces(buf);
173 }
174
175 if (dontFixDeadlocks && hadDeadlocks && !isRewardFile)
176 throw storm::exceptions::WrongFormatException() << "Some of the states do not have outgoing transitions.";
177
178 // Since we assume the transition rewards are for the transitions of the model, we copy the rowGroupIndices.
179 if (isRewardFile) {
180 for (uint_fast64_t node = lastSource + 1; node < modelInformation.getRowGroupCount(); node++) {
181 matrixBuilder.newRowGroup(modelInformation.getRowGroupIndices()[node]);
182 }
183 }
184
185 // Finally, build the actual matrix, test and return it.
186 storm::storage::SparseMatrix<ValueType> resultMatrix = matrixBuilder.build();
187
188 // Since we cannot check if each transition for which there is a reward in the reward file also exists in the transition matrix during parsing, we have to
189 // do it afterwards.
190 if (isRewardFile && !resultMatrix.isSubmatrixOf(modelInformation)) {
191 STORM_LOG_ERROR("There are rewards for non existent transitions given in the reward file.");
192 throw storm::exceptions::WrongFormatException() << "There are rewards for non existent transitions given in the reward file.";
193 }
194
195 return resultMatrix;
196}
197
198template<typename ValueType>
199template<typename MatrixValueType>
200typename NondeterministicSparseTransitionParser<ValueType>::FirstPassResult NondeterministicSparseTransitionParser<ValueType>::firstPass(
201 char const* buf, bool isRewardFile, storm::storage::SparseMatrix<MatrixValueType> const& modelInformation) {
202 // Check file header and extract number of transitions.
203
204 // Skip the format hint if it is there.
205 buf = trimWhitespaces(buf);
206 if (buf[0] < '0' || buf[0] > '9') {
207 buf = forwardToLineEnd(buf);
208 buf = trimWhitespaces(buf);
209 }
210
211 // Read all transitions.
212 uint_fast64_t source = 0, target = 0, choice = 0, lastChoice = 0, lastSource = 0, lastTarget = -1;
213 double val = 0.0;
214 typename NondeterministicSparseTransitionParser<ValueType>::FirstPassResult result;
215
216 // Since the first line is already a new choice but is not covered below, that has to be covered here.
217 result.choices = 1;
218
219 while (buf[0] != '\0') {
220 // Read source state and choice.
221 source = checked_strtol(buf, &buf);
222
223 // Read the name of the nondeterministic choice.
224 choice = checked_strtol(buf, &buf);
225
226 if (source < lastSource) {
227 STORM_LOG_ERROR("The current source state " << source << " is smaller than the last one " << lastSource << ".");
228 throw storm::exceptions::InvalidArgumentException()
229 << "The current source state " << source << " is smaller than the last one " << lastSource << ".";
230 }
231
232 // Check if we encountered a state index that is bigger than all previously seen.
233 if (source > result.highestStateIndex) {
234 result.highestStateIndex = source;
235 }
236
237 if (isRewardFile) {
238 // Make sure that the highest state index of the reward file is not higher than the highest state index of the corresponding model.
239 if (result.highestStateIndex > modelInformation.getColumnCount() - 1) {
240 STORM_LOG_ERROR("State index " << result.highestStateIndex << " found. This exceeds the highest state index of the model, which is "
241 << modelInformation.getColumnCount() - 1 << " .");
242 throw storm::exceptions::OutOfRangeException()
243 << "State index " << result.highestStateIndex << " found. This exceeds the highest state index of the model, which is "
244 << modelInformation.getColumnCount() - 1 << " .";
245 }
246
247 // If we have switched the source state, we possibly need to insert rows for skipped choices of the last
248 // source state.
249 if (source != lastSource) {
250 // number of choices skipped = number of choices of last state - number of choices read
251 result.choices +=
252 ((modelInformation.getRowGroupIndices())[lastSource + 1] - (modelInformation.getRowGroupIndices())[lastSource]) - (lastChoice + 1);
253 }
254
255 // If we skipped some states, we need to reserve empty rows for all their nondeterministic
256 // choices.
257 for (uint_fast64_t i = lastSource + 1; i < source; ++i) {
258 result.choices += ((modelInformation.getRowGroupIndices())[i + 1] - (modelInformation.getRowGroupIndices())[i]);
259 }
260
261 // If we advanced to the next state, but skipped some choices, we have to reserve rows
262 // for them.
263 if (source != lastSource) {
264 result.choices += choice + 1;
265 } else if (choice != lastChoice) {
266 result.choices += choice - lastChoice;
267 }
268 } else {
269 // If we have skipped some states, we need to reserve the space for the self-loop insertion
270 // in the second pass.
271 if (source > lastSource + 1) {
272 result.numberOfNonzeroEntries += source - lastSource - 1;
273 result.choices += source - lastSource - 1;
274 }
275
276 if (source != lastSource || choice != lastChoice) {
277 // If we have switched the source state or the nondeterministic choice, we need to
278 // reserve one row more.
279 ++result.choices;
280 }
281 }
282
283 // Read target and check if we encountered a state index that is bigger than all previously seen.
284 target = checked_strtol(buf, &buf);
285
286 if (target > result.highestStateIndex) {
287 result.highestStateIndex = target;
288 }
289
290 // Also, have we already seen this transition?
291 if (target == lastTarget && choice == lastChoice && source == lastSource) {
292 STORM_LOG_ERROR("The same transition (" << source << ", " << choice << ", " << target << ") is given twice.");
293 throw storm::exceptions::InvalidArgumentException() << "The same transition (" << source << ", " << choice << ", " << target << ") is given twice.";
294 }
295
296 // Read value and check whether it's positive.
297 val = checked_strtod(buf, &buf);
298 if (!isRewardFile && (val < 0.0 || val > 1.0)) {
299 STORM_LOG_ERROR("Expected a positive probability but got \"" << std::string(buf, 0, 16) << "\".");
300 NondeterministicSparseTransitionParser::FirstPassResult nullResult;
301 return nullResult;
302 } else if (val < 0.0) {
303 STORM_LOG_ERROR("Expected a positive reward value but got \"" << std::string(buf, 0, 16) << "\".");
304 NondeterministicSparseTransitionParser::FirstPassResult nullResult;
305 return nullResult;
306 }
307
308 lastChoice = choice;
309 lastSource = source;
310 lastTarget = target;
311
312 // Increase number of non-zero values.
313 result.numberOfNonzeroEntries++;
314
315 // The PRISM output format lists the name of the transition in the fourth column,
316 // but omits the fourth column if it is an internal action. In either case we can skip to the end of the line.
317 buf = forwardToLineEnd(buf);
318
319 buf = trimWhitespaces(buf);
320 }
321
322 if (isRewardFile) {
323 // If not all rows were filled for the last state, we need to insert them.
324 result.choices += ((modelInformation.getRowGroupIndices())[lastSource + 1] - (modelInformation.getRowGroupIndices())[lastSource]) - (lastChoice + 1);
325
326 // If we skipped some states, we need to reserve empty rows for all their nondeterministic
327 // choices.
328 for (uint_fast64_t i = lastSource + 1; i < modelInformation.getRowGroupIndices().size() - 1; ++i) {
329 result.choices += ((modelInformation.getRowGroupIndices())[i + 1] - (modelInformation.getRowGroupIndices())[i]);
330 }
331 }
332
333 return result;
334}
335
336template class NondeterministicSparseTransitionParser<double>;
338 std::string const& filename, storm::storage::SparseMatrix<double> const& modelInformation);
339template storm::storage::SparseMatrix<double> NondeterministicSparseTransitionParser<double>::parse(
340 std::string const& filename, bool isRewardFile, storm::storage::SparseMatrix<double> const& modelInformation);
341
342template class NondeterministicSparseTransitionParser<storm::Interval>;
343
344template storm::storage::SparseMatrix<storm::Interval> NondeterministicSparseTransitionParser<storm::Interval>::parseNondeterministicTransitionRewards<double>(
345 std::string const& filename, storm::storage::SparseMatrix<double> const& modelInformation);
346template storm::storage::SparseMatrix<storm::Interval> NondeterministicSparseTransitionParser<storm::Interval>::parse<double>(
347 std::string const& filename, bool isRewardFile, storm::storage::SparseMatrix<double> const& modelInformation);
348
349} // namespace parser
350} // namespace storm
Opens a file and maps it to memory providing a char* containing the file content.
Definition MappedFile.h:21
A class providing the functionality to parse the transitions of a nondeterministic model.
static storm::storage::SparseMatrix< ValueType > parseNondeterministicTransitions(std::string const &filename)
Load a nondeterministic transition system from file and create a sparse adjacency matrix whose entrie...
static storm::storage::SparseMatrix< ValueType > parseNondeterministicTransitionRewards(std::string const &filename, storm::storage::SparseMatrix< MatrixValueType > const &modelInformation)
Load a nondeterministic transition system from file and create a sparse adjacency matrix whose entrie...
A class that can be used to build a sparse matrix by adding value by value.
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)
A class that holds a possibly non-square matrix in the compressed row storage format.
index_type getEntryCount() const
Returns the number of entries in the matrix.
index_type getRowGroupCount() const
Returns the number of row groups in the matrix.
bool isSubmatrixOf(SparseMatrix< OtherValueType > const &matrix) const
Checks if the current matrix is a submatrix of the given matrix, where a matrix A is called a submatr...
index_type getColumnCount() const
Returns the number of columns of the matrix.
std::vector< index_type > const & getRowGroupIndices() const
Returns the grouping of rows of this matrix.
index_type getRowCount() const
Returns the number of rows of the matrix.
#define STORM_LOG_INFO(message)
Definition logging.h:24
#define STORM_LOG_ERROR(message)
Definition logging.h:26
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:74
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:39
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 * trimWhitespaces(char const *buf)
Skips spaces, tabs, newlines and carriage returns.
Definition cstring.cpp:66
A structure representing the result of the first pass of this parser.
uint_fast64_t numberOfNonzeroEntries
The total number of non-zero entries of the model.
uint_fast64_t highestStateIndex
The highest state index that appears in the model.
uint_fast64_t choices
The total number of nondeterministic choices within the transition system.