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