Storm 1.10.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
SparseMatrix.cpp
Go to the documentation of this file.
1#include <boost/functional/hash.hpp>
2
6
12
18
20
21#include <iterator>
22
23namespace storm {
24namespace storage {
25
26template<typename IndexType, typename ValueType>
27MatrixEntry<IndexType, ValueType>::MatrixEntry(IndexType column, ValueType value) : entry(column, value) {
28 // Intentionally left empty.
29}
30
31template<typename IndexType, typename ValueType>
32MatrixEntry<IndexType, ValueType>::MatrixEntry(std::pair<IndexType, ValueType>&& pair) : entry(std::move(pair)) {
33 // Intentionally left empty.
34}
35
36template<typename IndexType, typename ValueType>
38 return this->entry.first;
39}
40
41template<typename IndexType, typename ValueType>
42void MatrixEntry<IndexType, ValueType>::setColumn(IndexType const& column) {
43 this->entry.first = column;
44}
45
46template<typename IndexType, typename ValueType>
48 return this->entry.second;
49}
50
51template<typename IndexType, typename ValueType>
52void MatrixEntry<IndexType, ValueType>::setValue(ValueType const& value) {
53 this->entry.second = value;
54}
55
56template<typename IndexType, typename ValueType>
57std::pair<IndexType, ValueType> const& MatrixEntry<IndexType, ValueType>::getColumnValuePair() const {
58 return this->entry;
59}
60
61template<typename IndexType, typename ValueType>
63 return MatrixEntry(this->getColumn(), this->getValue() * factor);
64}
65
66template<typename IndexType, typename ValueType>
68 return this->entry.first == other.entry.first && this->entry.second == other.entry.second;
69}
70
71template<typename IndexType, typename ValueType>
73 return !(*this == other);
74}
75
76template<typename IndexTypePrime, typename ValueTypePrime>
77std::ostream& operator<<(std::ostream& out, MatrixEntry<IndexTypePrime, ValueTypePrime> const& entry) {
78 out << "(" << entry.getColumn() << ", " << entry.getValue() << ")";
79 return out;
80}
81
82template<typename ValueType>
83SparseMatrixBuilder<ValueType>::SparseMatrixBuilder(index_type rows, index_type columns, index_type entries, bool forceDimensions, bool hasCustomRowGrouping,
84 index_type rowGroups)
85 : initialRowCountSet(rows != 0),
86 initialRowCount(rows),
87 initialColumnCountSet(columns != 0),
88 initialColumnCount(columns),
89 initialEntryCountSet(entries != 0),
90 initialEntryCount(entries),
91 forceInitialDimensions(forceDimensions),
92 hasCustomRowGrouping(hasCustomRowGrouping),
93 initialRowGroupCountSet(rowGroups != 0),
94 initialRowGroupCount(rowGroups),
95 rowGroupIndices(),
96 columnsAndValues(),
97 rowIndications(),
98 currentEntryCount(0),
99 lastRow(0),
100 lastColumn(0),
101 highestColumn(0),
102 currentRowGroupCount(0) {
103 // Prepare the internal storage.
104 if (initialRowCountSet) {
105 rowIndications.reserve(initialRowCount + 1);
106 }
107 if (initialEntryCountSet) {
108 columnsAndValues.reserve(initialEntryCount);
109 }
110 if (hasCustomRowGrouping) {
111 rowGroupIndices = std::vector<index_type>();
112 }
113 if (initialRowGroupCountSet && hasCustomRowGrouping) {
114 rowGroupIndices.get().reserve(initialRowGroupCount + 1);
115 }
116 rowIndications.push_back(0);
117}
118
119template<typename ValueType>
121 : initialRowCountSet(false),
122 initialRowCount(0),
123 initialColumnCountSet(false),
124 initialColumnCount(0),
125 initialEntryCountSet(false),
126 initialEntryCount(0),
127 forceInitialDimensions(false),
128 hasCustomRowGrouping(!matrix.trivialRowGrouping),
129 initialRowGroupCountSet(false),
130 initialRowGroupCount(0),
131 rowGroupIndices(),
132 columnsAndValues(std::move(matrix.columnsAndValues)),
133 rowIndications(std::move(matrix.rowIndications)),
134 currentEntryCount(matrix.entryCount),
135 currentRowGroupCount() {
136 lastRow = matrix.rowCount == 0 ? 0 : matrix.rowCount - 1;
137 lastColumn = columnsAndValues.empty() ? 0 : columnsAndValues.back().getColumn();
138 highestColumn = matrix.getColumnCount() == 0 ? 0 : matrix.getColumnCount() - 1;
139
140 // If the matrix has a custom row grouping, we move it and remove the last element to make it 'open' again.
141 if (hasCustomRowGrouping) {
142 rowGroupIndices = std::move(matrix.rowGroupIndices);
143 if (!rowGroupIndices->empty()) {
144 rowGroupIndices.get().pop_back();
145 }
146 currentRowGroupCount = rowGroupIndices->empty() ? 0 : rowGroupIndices.get().size() - 1;
147 }
148
149 // Likewise, we need to 'open' the row indications again.
150 if (!rowIndications.empty()) {
151 rowIndications.pop_back();
152 }
153}
154
155template<typename ValueType>
156void SparseMatrixBuilder<ValueType>::addNextValue(index_type row, index_type column, ValueType const& value) {
157 // Check that we did not move backwards wrt. the row.
158 STORM_LOG_THROW(row >= lastRow, storm::exceptions::InvalidArgumentException,
159 "Adding an element in row " << row << ", but an element in row " << lastRow << " has already been added.");
160 STORM_LOG_ASSERT(columnsAndValues.size() == currentEntryCount, "Unexpected size of columnsAndValues vector.");
161
162 // Check if a diagonal entry shall be inserted before
163 if (pendingDiagonalEntry) {
164 index_type diagColumn = hasCustomRowGrouping ? currentRowGroupCount - 1 : lastRow;
165 if (row > lastRow || column >= diagColumn) {
166 ValueType diagValue = std::move(pendingDiagonalEntry.get());
167 pendingDiagonalEntry = boost::none;
168 // Add the pending diagonal value now
169 if (row == lastRow && column == diagColumn) {
170 // The currently added value coincides with the diagonal entry!
171 // We add up the values and repeat this call.
172 addNextValue(row, column, diagValue + value);
173 // We return here because the above call already did all the work.
174 return;
175 } else {
176 addNextValue(lastRow, diagColumn, diagValue);
177 }
178 }
179 }
180
181 // If the element is in the same row, but was not inserted in the correct order, we need to fix the row after
182 // the insertion.
183 bool fixCurrentRow = row == lastRow && column < lastColumn;
184 // If the element is in the same row and column as the previous entry, we add them up...
185 // unless there is no entry in this row yet, which might happen either for the very first entry or when only a diagonal value has been added
186 if (row == lastRow && column == lastColumn && rowIndications.back() < currentEntryCount) {
187 columnsAndValues.back().setValue(columnsAndValues.back().getValue() + value);
188 } else {
189 // If we switched to another row, we have to adjust the missing entries in the row indices vector.
190 if (row != lastRow) {
191 // Otherwise, we need to push the correct values to the vectors, which might trigger reallocations.
192 assert(rowIndications.size() == lastRow + 1);
193 rowIndications.resize(row + 1, currentEntryCount);
194 lastRow = row;
195 }
196
197 lastColumn = column;
198
199 // Finally, set the element and increase the current size.
200 columnsAndValues.emplace_back(column, value);
201 highestColumn = std::max(highestColumn, column);
202 ++currentEntryCount;
203
204 // If we need to fix the row, do so now.
205 if (fixCurrentRow) {
206 // TODO we fix this row directly after the out-of-order insertion, but the code does not exploit that fact.
207 STORM_LOG_TRACE("Fix row " << row << " as column " << column << " is added out-of-order.");
208 // First, we sort according to columns.
209 std::sort(columnsAndValues.begin() + rowIndications.back(), columnsAndValues.end(),
211 return a.getColumn() < b.getColumn();
212 });
213
214 auto insertIt = columnsAndValues.begin() + rowIndications.back();
215 uint64_t elementsToRemove = 0;
216 for (auto it = insertIt + 1; it != columnsAndValues.end(); ++it) {
217 // Iterate over all entries in this last row and detect duplicates.
218 if (it->getColumn() == insertIt->getColumn()) {
219 // This entry is a duplicate of the column. Update the previous entry.
220 insertIt->setValue(insertIt->getValue() + it->getValue());
221 elementsToRemove++;
222 } else {
223 insertIt = it;
224 }
225 }
226 // Then, we eliminate those duplicate entries.
227 // We cast the result of std::unique to void to silence a warning issued due to a [[nodiscard]] attribute
228 static_cast<void>(std::unique(columnsAndValues.begin() + rowIndications.back(), columnsAndValues.end(),
230 storm::storage::MatrixEntry<index_type, ValueType> const& b) { return a.getColumn() == b.getColumn(); }));
231
232 if (elementsToRemove > 0) {
233 STORM_LOG_WARN("Unordered insertion into matrix builder caused duplicate entries.");
234 currentEntryCount -= elementsToRemove;
235 columnsAndValues.resize(columnsAndValues.size() - elementsToRemove);
236 }
237 lastColumn = columnsAndValues.back().getColumn();
238 }
239 }
240
241 // In case we did not expect this value, we throw an exception.
242 if (forceInitialDimensions) {
243 STORM_LOG_THROW(!initialRowCountSet || lastRow < initialRowCount, storm::exceptions::OutOfRangeException,
244 "Cannot insert value at illegal row " << lastRow << ".");
245 STORM_LOG_THROW(!initialColumnCountSet || lastColumn < initialColumnCount, storm::exceptions::OutOfRangeException,
246 "Cannot insert value at illegal column " << lastColumn << ".");
247 STORM_LOG_THROW(!initialEntryCountSet || currentEntryCount <= initialEntryCount, storm::exceptions::OutOfRangeException,
248 "Too many entries in matrix, expected only " << initialEntryCount << ".");
249 }
250}
251
252template<typename ValueType>
254 STORM_LOG_THROW(hasCustomRowGrouping, storm::exceptions::InvalidStateException, "Matrix was not created to have a custom row grouping.");
255 STORM_LOG_THROW(startingRow >= lastRow, storm::exceptions::InvalidStateException, "Illegal row group with negative size.");
256
257 // If there still is a pending diagonal entry, we need to add it now (otherwise, the correct diagonal column will be unclear)
258 if (pendingDiagonalEntry) {
259 STORM_LOG_ASSERT(currentRowGroupCount > 0, "Diagonal entry was set before opening the first row group.");
260 index_type diagColumn = currentRowGroupCount - 1;
261 ValueType diagValue = std::move(pendingDiagonalEntry.get());
262 pendingDiagonalEntry = boost::none; // clear now, so addNextValue works properly
263 addNextValue(lastRow, diagColumn, diagValue);
264 }
265
266 rowGroupIndices.get().push_back(startingRow);
267 ++currentRowGroupCount;
268
269 // Handle the case where the previous row group ends with one or more empty rows
270 if (lastRow + 1 < startingRow) {
271 // Close all rows from the most recent one to the starting row.
272 assert(rowIndications.size() == lastRow + 1);
273 rowIndications.resize(startingRow, currentEntryCount);
274 // Reset the most recently seen row/column to allow for proper insertion of the following elements.
275 lastRow = startingRow - 1;
276 lastColumn = 0;
277 }
278}
279
280template<typename ValueType>
282 index_type overriddenRowGroupCount) {
283 // If there still is a pending diagonal entry, we need to add it now
284 if (pendingDiagonalEntry) {
285 index_type diagColumn = hasCustomRowGrouping ? currentRowGroupCount - 1 : lastRow;
286 ValueType diagValue = std::move(pendingDiagonalEntry.get());
287 pendingDiagonalEntry = boost::none; // clear now, so addNextValue works properly
288 addNextValue(lastRow, diagColumn, diagValue);
289 }
290
291 bool hasEntries = currentEntryCount != 0;
292
293 uint_fast64_t rowCount = hasEntries ? lastRow + 1 : 0;
294
295 // If the last row group was empty, we need to add one more to the row count, because otherwise this empty row is not counted.
296 if (hasCustomRowGrouping) {
297 if (lastRow < rowGroupIndices->back()) {
298 ++rowCount;
299 }
300 }
301
302 if (initialRowCountSet && forceInitialDimensions) {
303 STORM_LOG_THROW(rowCount <= initialRowCount, storm::exceptions::InvalidStateException,
304 "Expected not more than " << initialRowCount << " rows, but got " << rowCount << ".");
305 rowCount = std::max(rowCount, initialRowCount);
306 }
307
308 rowCount = std::max(rowCount, overriddenRowCount);
309
310 // If the current row count was overridden, we may need to add empty rows.
311 for (index_type i = lastRow + 1; i < rowCount; ++i) {
312 rowIndications.push_back(currentEntryCount);
313 }
314
315 // We put a sentinel element at the last position of the row indices array. This eases iteration work,
316 // as now the indices of row i are always between rowIndications[i] and rowIndications[i + 1], also for
317 // the first and last row.
318 if (rowCount > 0) {
319 rowIndications.push_back(currentEntryCount);
320 }
321 STORM_LOG_ASSERT(rowCount == rowIndications.size() - 1,
322 "Wrong sizes of row indications vector: (Rowcount) " << rowCount << " != " << (rowIndications.size() - 1) << " (Vector).");
323 uint_fast64_t columnCount = hasEntries ? highestColumn + 1 : 0;
324 if (initialColumnCountSet && forceInitialDimensions) {
325 STORM_LOG_THROW(columnCount <= initialColumnCount, storm::exceptions::InvalidStateException,
326 "Expected not more than " << initialColumnCount << " columns, but got " << columnCount << ".");
327 columnCount = std::max(columnCount, initialColumnCount);
328 }
329 columnCount = std::max(columnCount, overriddenColumnCount);
330
331 uint_fast64_t entryCount = currentEntryCount;
332 if (initialEntryCountSet && forceInitialDimensions) {
333 STORM_LOG_THROW(entryCount == initialEntryCount, storm::exceptions::InvalidStateException,
334 "Expected " << initialEntryCount << " entries, but got " << entryCount << ".");
335 }
336
337 // Check whether row groups are missing some entries.
338 if (hasCustomRowGrouping) {
339 uint_fast64_t rowGroupCount = currentRowGroupCount;
340 if (initialRowGroupCountSet && forceInitialDimensions) {
341 STORM_LOG_THROW(rowGroupCount <= initialRowGroupCount, storm::exceptions::InvalidStateException,
342 "Expected not more than " << initialRowGroupCount << " row groups, but got " << rowGroupCount << ".");
343 rowGroupCount = std::max(rowGroupCount, initialRowGroupCount);
344 }
345 rowGroupCount = std::max(rowGroupCount, overriddenRowGroupCount);
346
347 for (index_type i = currentRowGroupCount; i <= rowGroupCount; ++i) {
348 rowGroupIndices.get().push_back(rowCount);
349 }
350 }
351
352 return SparseMatrix<ValueType>(columnCount, std::move(rowIndications), std::move(columnsAndValues), std::move(rowGroupIndices));
353}
354
355template<typename ValueType>
359
360template<typename ValueType>
362 if (this->hasCustomRowGrouping) {
363 return currentRowGroupCount;
364 } else {
365 return getLastRow() + 1;
366 }
367}
368
369template<typename ValueType>
371 return lastColumn;
372}
373
374// Debug method for printing the current matrix
375template<typename ValueType>
376void print(std::vector<typename SparseMatrix<ValueType>::index_type> const& rowGroupIndices,
377 std::vector<MatrixEntry<typename SparseMatrix<ValueType>::index_type, typename SparseMatrix<ValueType>::value_type>> const& columnsAndValues,
378 std::vector<typename SparseMatrix<ValueType>::index_type> const& rowIndications) {
379 typename SparseMatrix<ValueType>::index_type endGroups;
381 // Iterate over all row groups.
382 for (typename SparseMatrix<ValueType>::index_type group = 0; group < rowGroupIndices.size(); ++group) {
383 std::cout << "\t---- group " << group << "/" << (rowGroupIndices.size() - 1) << " ---- \n";
384 endGroups = group < rowGroupIndices.size() - 1 ? rowGroupIndices[group + 1] : rowIndications.size();
385 // Iterate over all rows in a row group
386 for (typename SparseMatrix<ValueType>::index_type i = rowGroupIndices[group]; i < endGroups; ++i) {
387 endRows = i < rowIndications.size() - 1 ? rowIndications[i + 1] : columnsAndValues.size();
388 // Print the actual row.
389 std::cout << "Row " << i << " (" << rowIndications[i] << " - " << endRows << ")" << ": ";
390 for (typename SparseMatrix<ValueType>::index_type pos = rowIndications[i]; pos < endRows; ++pos) {
391 std::cout << "(" << columnsAndValues[pos].getColumn() << ": " << columnsAndValues[pos].getValue() << ") ";
392 }
393 std::cout << '\n';
394 }
395 }
396}
397
398template<typename ValueType>
399void SparseMatrixBuilder<ValueType>::replaceColumns(std::vector<index_type> const& replacements, index_type offset) {
400 index_type maxColumn = 0;
401
402 for (index_type row = 0; row < rowIndications.size(); ++row) {
403 bool changed = false;
404 auto startRow = std::next(columnsAndValues.begin(), rowIndications[row]);
405 auto endRow = row < rowIndications.size() - 1 ? std::next(columnsAndValues.begin(), rowIndications[row + 1]) : columnsAndValues.end();
406 for (auto entry = startRow; entry != endRow; ++entry) {
407 if (entry->getColumn() >= offset) {
408 // Change column
409 entry->setColumn(replacements[entry->getColumn() - offset]);
410 changed = true;
411 }
412 maxColumn = std::max(maxColumn, entry->getColumn());
414 if (changed) {
415 // Sort columns in row
416 std::sort(startRow, endRow,
418 // Assert no equal elements
419 STORM_LOG_ASSERT(std::is_sorted(startRow, endRow,
421 return a.getColumn() < b.getColumn();
422 }),
423 "Columns not sorted.");
424 }
425 }
426
427 highestColumn = maxColumn;
428 lastColumn = columnsAndValues.empty() ? 0 : columnsAndValues.back().getColumn();
429}
430
431template<typename ValueType>
433 STORM_LOG_THROW(row >= lastRow, storm::exceptions::InvalidArgumentException,
434 "Adding a diagonal element in row " << row << ", but an element in row " << lastRow << " has already been added.");
435 if (pendingDiagonalEntry) {
436 if (row == lastRow) {
437 // Add the two diagonal entries, nothing else to be done.
438 pendingDiagonalEntry.get() += value;
439 return;
440 } else {
441 // add the pending entry
442 index_type column = hasCustomRowGrouping ? currentRowGroupCount - 1 : lastRow;
443 ValueType diagValue = std::move(pendingDiagonalEntry.get());
444 pendingDiagonalEntry = boost::none; // clear now, so addNextValue works properly
445 addNextValue(lastRow, column, diagValue);
446 }
448 pendingDiagonalEntry = value;
449 if (lastRow != row) {
450 assert(rowIndications.size() == lastRow + 1);
451 rowIndications.resize(row + 1, currentEntryCount);
452 lastRow = row;
453 lastColumn = 0;
454 }
455}
457template<typename ValueType>
458SparseMatrix<ValueType>::rows::rows(iterator begin, index_type entryCount) : beginIterator(begin), entryCount(entryCount) {
459 // Intentionally left empty.
460}
461
462template<typename ValueType>
466
467template<typename ValueType>
469 return beginIterator + entryCount;
470}
471
472template<typename ValueType>
476
477template<typename ValueType>
478SparseMatrix<ValueType>::const_rows::const_rows(const_iterator begin, index_type entryCount) : beginIterator(begin), entryCount(entryCount) {
479 // Intentionally left empty.
480}
481
482template<typename ValueType>
484 return beginIterator;
485}
486
487template<typename ValueType>
489 return beginIterator + entryCount;
490}
491
492template<typename ValueType>
496
497template<typename ValueType>
499 : rowCount(0), columnCount(0), entryCount(0), nonzeroEntryCount(0), columnsAndValues(), rowIndications(), rowGroupIndices() {
500 // Intentionally left empty.
501}
502
503template<typename ValueType>
505 : rowCount(other.rowCount),
506 columnCount(other.columnCount),
507 entryCount(other.entryCount),
508 nonzeroEntryCount(other.nonzeroEntryCount),
509 columnsAndValues(other.columnsAndValues),
510 rowIndications(other.rowIndications),
511 trivialRowGrouping(other.trivialRowGrouping),
512 rowGroupIndices(other.rowGroupIndices) {
513 // Intentionally left empty.
515
516template<typename ValueType>
517SparseMatrix<ValueType>::SparseMatrix(SparseMatrix<value_type> const& other, bool insertDiagonalElements) {
518 storm::storage::BitVector rowConstraint(other.getRowCount(), true);
519 storm::storage::BitVector columnConstraint(other.getColumnCount(), true);
520 *this = other.getSubmatrix(false, rowConstraint, columnConstraint, insertDiagonalElements);
522
523template<typename ValueType>
525 : rowCount(other.rowCount),
526 columnCount(other.columnCount),
527 entryCount(other.entryCount),
528 nonzeroEntryCount(other.nonzeroEntryCount),
529 columnsAndValues(std::move(other.columnsAndValues)),
530 rowIndications(std::move(other.rowIndications)),
531 trivialRowGrouping(other.trivialRowGrouping),
532 rowGroupIndices(std::move(other.rowGroupIndices)) {
533 // Now update the source matrix
534 other.rowCount = 0;
535 other.columnCount = 0;
536 other.entryCount = 0;
537}
538
539template<typename ValueType>
540SparseMatrix<ValueType>::SparseMatrix(index_type columnCount, std::vector<index_type> const& rowIndications,
541 std::vector<MatrixEntry<index_type, ValueType>> const& columnsAndValues,
542 boost::optional<std::vector<index_type>> const& rowGroupIndices)
543 : rowCount(rowIndications.size() - 1),
544 columnCount(columnCount),
545 entryCount(columnsAndValues.size()),
546 nonzeroEntryCount(0),
547 columnsAndValues(columnsAndValues),
548 rowIndications(rowIndications),
549 trivialRowGrouping(!rowGroupIndices),
550 rowGroupIndices(rowGroupIndices) {
552}
553
554template<typename ValueType>
555SparseMatrix<ValueType>::SparseMatrix(index_type columnCount, std::vector<index_type>&& rowIndications,
556 std::vector<MatrixEntry<index_type, ValueType>>&& columnsAndValues,
557 boost::optional<std::vector<index_type>>&& rowGroupIndices)
558 : columnCount(columnCount),
559 nonzeroEntryCount(0),
560 columnsAndValues(std::move(columnsAndValues)),
561 rowIndications(std::move(rowIndications)),
562 rowGroupIndices(std::move(rowGroupIndices)) {
563 // Initialize some variables here which depend on other variables
564 // This way we are more robust against different initialization orders
565 this->rowCount = this->rowIndications.size() - 1;
566 this->entryCount = this->columnsAndValues.size();
567 this->trivialRowGrouping = !this->rowGroupIndices;
569}
570
571template<typename ValueType>
573 // Only perform assignment if source and target are not the same.
574 if (this != &other) {
575 rowCount = other.rowCount;
576 columnCount = other.columnCount;
577 entryCount = other.entryCount;
578 nonzeroEntryCount = other.nonzeroEntryCount;
579
580 columnsAndValues = other.columnsAndValues;
581 rowIndications = other.rowIndications;
582 rowGroupIndices = other.rowGroupIndices;
583 trivialRowGrouping = other.trivialRowGrouping;
584 }
585 return *this;
587
588template<typename ValueType>
590 // Only perform assignment if source and target are not the same.
591 if (this != &other) {
592 rowCount = other.rowCount;
593 columnCount = other.columnCount;
594 entryCount = other.entryCount;
595 nonzeroEntryCount = other.nonzeroEntryCount;
596
597 columnsAndValues = std::move(other.columnsAndValues);
598 rowIndications = std::move(other.rowIndications);
599 rowGroupIndices = std::move(other.rowGroupIndices);
600 trivialRowGrouping = other.trivialRowGrouping;
601 }
602 return *this;
603}
604
605template<typename ValueType>
607 if (this == &other) {
608 return true;
609 }
610
611 bool equalityResult = true;
612
613 equalityResult &= this->getRowCount() == other.getRowCount();
614 if (!equalityResult) {
615 return false;
616 }
617 equalityResult &= this->getColumnCount() == other.getColumnCount();
618 if (!equalityResult) {
619 return false;
621 if (!this->hasTrivialRowGrouping() && !other.hasTrivialRowGrouping()) {
622 equalityResult &= this->getRowGroupIndices() == other.getRowGroupIndices();
623 } else {
624 equalityResult &= this->hasTrivialRowGrouping() && other.hasTrivialRowGrouping();
625 }
626 if (!equalityResult) {
627 return false;
628 }
629
630 // For the actual contents, we need to do a little bit more work, because we want to ignore elements that
631 // are set to zero, please they may be represented implicitly in the other matrix.
632 for (index_type row = 0; row < this->getRowCount(); ++row) {
633 for (const_iterator it1 = this->begin(row), ite1 = this->end(row), it2 = other.begin(row), ite2 = other.end(row); it1 != ite1 && it2 != ite2;
634 ++it1, ++it2) {
635 // Skip over all zero entries in both matrices.
636 while (it1 != ite1 && storm::utility::isZero(it1->getValue())) {
637 ++it1;
638 }
639 while (it2 != ite2 && storm::utility::isZero(it2->getValue())) {
640 ++it2;
641 }
642 if ((it1 == ite1) || (it2 == ite2)) {
643 equalityResult = (it1 == ite1) ^ (it2 == ite2);
644 break;
645 } else {
646 if (it1->getColumn() != it2->getColumn() || it1->getValue() != it2->getValue()) {
647 equalityResult = false;
648 break;
649 }
650 }
651 }
652 if (!equalityResult) {
653 return false;
654 }
655 }
657 return equalityResult;
658}
659
660template<typename ValueType>
664
665template<typename ValueType>
669
670template<typename ValueType>
675template<typename ValueType>
677 index_type result = 0;
678 if (!this->hasTrivialRowGrouping()) {
679 for (auto row : this->getRowGroupIndices(group)) {
680 result += (this->rowIndications[row + 1] - this->rowIndications[row]);
681 }
682 } else {
683 result += (this->rowIndications[group + 1] - this->rowIndications[group]);
684 }
685 return result;
686}
687
688template<typename ValueType>
690 return nonzeroEntryCount;
691}
693template<typename ValueType>
695 this->nonzeroEntryCount = 0;
696 for (auto const& element : *this) {
697 if (element.getValue() != storm::utility::zero<ValueType>()) {
698 ++this->nonzeroEntryCount;
699 }
700 }
702
703template<typename ValueType>
704void SparseMatrix<ValueType>::updateNonzeroEntryCount(std::make_signed<index_type>::type difference) {
705 this->nonzeroEntryCount += difference;
706}
707
708template<typename ValueType>
710 this->nonzeroEntryCount = 0;
711 this->columnCount = 0;
712 for (auto const& element : *this) {
713 if (element.getValue() != storm::utility::zero<ValueType>()) {
714 ++this->nonzeroEntryCount;
715 this->columnCount = std::max(element.getColumn() + 1, this->columnCount);
716 }
717 }
718}
719
720template<typename ValueType>
722 if (!this->hasTrivialRowGrouping()) {
723 return rowGroupIndices.get().size() - 1;
724 } else {
725 return rowCount;
726 }
727}
728
729template<typename ValueType>
733
734template<typename ValueType>
736 if (this->hasTrivialRowGrouping()) {
737 return 1;
738 }
739 index_type res = 0;
740 index_type previousGroupStart = 0;
741 for (auto const& i : rowGroupIndices.get()) {
742 res = std::max(res, i - previousGroupStart);
743 previousGroupStart = i;
744 }
745 return res;
746}
747
748template<typename ValueType>
750 if (this->hasTrivialRowGrouping()) {
751 return groupConstraint.getNumberOfSetBits();
752 }
753 index_type numRows = 0;
754 index_type rowGroupIndex = groupConstraint.getNextSetIndex(0);
755 while (rowGroupIndex < this->getRowGroupCount()) {
756 index_type start = this->getRowGroupIndices()[rowGroupIndex];
757 rowGroupIndex = groupConstraint.getNextUnsetIndex(rowGroupIndex + 1);
758 index_type end = this->getRowGroupIndices()[rowGroupIndex];
759 // All rows with index in [start,end) are selected.
760 numRows += end - start;
761 rowGroupIndex = groupConstraint.getNextSetIndex(rowGroupIndex + 1);
763 return numRows;
764}
765
766template<typename ValueType>
767std::vector<typename SparseMatrix<ValueType>::index_type> const& SparseMatrix<ValueType>::getRowGroupIndices() const {
768 // If there is no current row grouping, we need to create it.
769 if (!this->rowGroupIndices) {
770 STORM_LOG_ASSERT(trivialRowGrouping, "Only trivial row-groupings can be constructed on-the-fly.");
771 this->rowGroupIndices = storm::utility::vector::buildVectorForRange(static_cast<index_type>(0), this->getRowGroupCount() + 1);
772 }
773 return rowGroupIndices.get();
774}
775
776template<typename ValueType>
777boost::integer_range<typename SparseMatrix<ValueType>::index_type> SparseMatrix<ValueType>::getRowGroupIndices(index_type group) const {
778 STORM_LOG_ASSERT(group < this->getRowGroupCount(),
779 "Invalid row group index:" << group << ". Only " << this->getRowGroupCount() << " row groups available.");
780 if (this->rowGroupIndices) {
781 return boost::irange(rowGroupIndices.get()[group], rowGroupIndices.get()[group + 1]);
782 } else {
783 return boost::irange(group, group + 1);
784 }
786
787template<typename ValueType>
788std::vector<typename SparseMatrix<ValueType>::index_type> SparseMatrix<ValueType>::swapRowGroupIndices(std::vector<index_type>&& newRowGrouping) {
789 std::vector<index_type> result;
790 if (this->rowGroupIndices) {
791 result = std::move(rowGroupIndices.get());
792 rowGroupIndices = std::move(newRowGrouping);
794 return result;
795}
796
797template<typename ValueType>
798void SparseMatrix<ValueType>::setRowGroupIndices(std::vector<index_type> const& newRowGroupIndices) {
799 trivialRowGrouping = false;
800 rowGroupIndices = newRowGroupIndices;
801}
802
803template<typename ValueType>
805 return trivialRowGrouping;
806}
807
808template<typename ValueType>
810 if (trivialRowGrouping) {
812 !rowGroupIndices || rowGroupIndices.get() == storm::utility::vector::buildVectorForRange(static_cast<index_type>(0), this->getRowGroupCount() + 1),
813 "Row grouping is supposed to be trivial but actually it is not.");
814 } else {
815 trivialRowGrouping = true;
816 rowGroupIndices = boost::none;
817 }
818}
819
820template<typename ValueType>
822 storm::storage::BitVector res(this->getRowCount(), false);
823 for (auto group : groupConstraint) {
824 res.setMultiple(getRowGroupIndices()[group], getRowGroupSize(group));
826 return res;
827}
828
829template<typename ValueType>
831 storm::storage::BitVector const& columnConstraint) const {
832 storm::storage::BitVector result(this->getRowCount(), false);
833 for (auto group : groupConstraint) {
834 for (auto row : this->getRowGroupIndices(group)) {
835 bool choiceSatisfiesColumnConstraint = true;
836 for (auto const& entry : this->getRow(row)) {
837 if (!columnConstraint.get(entry.getColumn())) {
838 choiceSatisfiesColumnConstraint = false;
839 break;
840 }
841 }
842 if (choiceSatisfiesColumnConstraint) {
843 result.set(row, true);
844 }
846 }
847 return result;
848}
849
850template<typename ValueType>
852 STORM_LOG_ASSERT(!this->hasTrivialRowGrouping(), "Tried to get a row group filter but this matrix does not have row groups");
853 storm::storage::BitVector result(this->getRowGroupCount(), false);
854 auto const& groupIndices = this->getRowGroupIndices();
855 if (setIfForAllRowsInGroup) {
856 for (uint64_t group = 0; group < this->getRowGroupCount(); ++group) {
857 if (rowConstraint.getNextUnsetIndex(groupIndices[group]) >= groupIndices[group + 1]) {
858 // All rows within this group are set
859 result.set(group, true);
860 }
862 } else {
863 for (uint64_t group = 0; group < this->getRowGroupCount(); ++group) {
864 if (rowConstraint.getNextSetIndex(groupIndices[group]) < groupIndices[group + 1]) {
865 // Some row is set
866 result.set(group, true);
867 }
869 }
870 return result;
871}
872
873template<typename ValueType>
875 // First transform ALL rows without dropping zero entries, then drop zero entries once
876 // This prevents iteration over the whole matrix every time an entry is set to zero.
877 for (auto row : rows) {
878 makeRowDirac(row, row, false);
879 }
880 if (dropZeroEntries) {
881 this->dropZeroEntries();
882 }
883}
884
885template<typename ValueType>
887 // First transform ALL rows without dropping zero entries, then drop zero entries once.
888 // This prevents iteration over the whole matrix every time an entry is set to zero.
889 if (!this->hasTrivialRowGrouping()) {
890 for (auto rowGroup : rowGroupConstraint) {
891 for (index_type row = this->getRowGroupIndices()[rowGroup]; row < this->getRowGroupIndices()[rowGroup + 1]; ++row) {
892 makeRowDirac(row, rowGroup, false);
893 }
894 }
895 } else {
896 for (auto rowGroup : rowGroupConstraint) {
897 makeRowDirac(rowGroup, rowGroup, false);
898 }
899 }
901 this->dropZeroEntries();
902 }
903}
904
905template<typename ValueType>
907 iterator columnValuePtr = this->begin(row);
908 iterator columnValuePtrEnd = this->end(row);
909
910 // If the row has no elements in it, we cannot make it absorbing, because we would need to move all elements
911 // in the vector of nonzeros otherwise.
912 if (columnValuePtr >= columnValuePtrEnd) {
913 throw storm::exceptions::InvalidStateException()
914 << "Illegal call to SparseMatrix::makeRowDirac: cannot make row " << row << " absorbing, because there is no entry in this row.";
915 }
916 iterator lastColumnValuePtr = this->end(row) - 1;
917
918 // If there is at least one entry in this row, we can set it to one, modify its column value to the
919 // one given by the parameter and set all subsequent elements of this row to zero.
920 // However, we want to preserve that column indices within a row are ascending, so we pick an entry that is close to the desired column index
921 while (columnValuePtr->getColumn() < column && columnValuePtr != lastColumnValuePtr) {
922 if (!storm::utility::isZero(columnValuePtr->getValue())) {
923 --this->nonzeroEntryCount;
924 }
925 columnValuePtr->setValue(storm::utility::zero<ValueType>());
926 ++columnValuePtr;
927 }
928 // At this point, we have found the first entry whose column is >= the desired column (or the last entry of the row, if no such column exist)
929 if (storm::utility::isZero(columnValuePtr->getValue())) {
930 ++this->nonzeroEntryCount;
932 columnValuePtr->setValue(storm::utility::one<ValueType>());
933 columnValuePtr->setColumn(column);
934 for (++columnValuePtr; columnValuePtr != columnValuePtrEnd; ++columnValuePtr) {
935 if (!storm::utility::isZero(columnValuePtr->getValue())) {
936 --this->nonzeroEntryCount;
937 }
938 columnValuePtr->setValue(storm::utility::zero<ValueType>());
939 }
940 if (dropZeroEntries) {
942 }
943}
944
945template<typename ValueType>
947 const_iterator end1 = this->end(i1);
948 const_iterator end2 = this->end(i2);
949 const_iterator it1 = this->begin(i1);
950 const_iterator it2 = this->begin(i2);
951 for (; it1 != end1 && it2 != end2; ++it1, ++it2) {
952 if (*it1 != *it2) {
953 return false;
954 }
956 if (it1 == end1 && it2 == end2) {
957 return true;
958 }
959 return false;
960}
961
962template<typename ValueType>
964 BitVector bv(this->getRowCount());
965 for (size_t rowgroup = 0; rowgroup < this->getRowGroupCount(); ++rowgroup) {
966 for (size_t row1 = this->getRowGroupIndices().at(rowgroup); row1 < this->getRowGroupIndices().at(rowgroup + 1); ++row1) {
967 for (size_t row2 = row1; row2 < this->getRowGroupIndices().at(rowgroup + 1); ++row2) {
968 if (compareRows(row1, row2)) {
969 bv.set(row2);
970 }
971 }
972 }
974 return bv;
975}
976
977template<typename ValueType>
979 if (row1 == row2) {
980 return;
981 }
982
983 // Get the index of the row that has more / less entries than the other.
984 index_type largerRow = getRow(row1).getNumberOfEntries() > getRow(row2).getNumberOfEntries() ? row1 : row2;
985 index_type smallerRow = largerRow == row1 ? row2 : row1;
986 index_type rowSizeDifference = getRow(largerRow).getNumberOfEntries() - getRow(smallerRow).getNumberOfEntries();
987
988 // Save contents of larger row.
989 auto copyRow = getRow(largerRow);
990 std::vector<MatrixEntry<index_type, value_type>> largerRowContents(copyRow.begin(), copyRow.end());
991
992 if (largerRow < smallerRow) {
993 auto writeIt = getRows(largerRow, smallerRow + 1).begin();
994
995 // Write smaller row to its new position.
996 for (auto& smallerRowEntry : getRow(smallerRow)) {
997 *writeIt = std::move(smallerRowEntry);
998 ++writeIt;
999 }
1001 // Write the intermediate rows into their correct position.
1002 if (!storm::utility::isZero(rowSizeDifference)) {
1003 for (auto& intermediateRowEntry : getRows(largerRow + 1, smallerRow)) {
1004 *writeIt = std::move(intermediateRowEntry);
1005 ++writeIt;
1006 }
1007 } else {
1008 // skip the intermediate rows
1009 writeIt = getRow(smallerRow).begin();
1010 }
1011
1012 // Write the larger row to its new position.
1013 for (auto& largerRowEntry : largerRowContents) {
1014 *writeIt = std::move(largerRowEntry);
1015 ++writeIt;
1016 }
1017
1018 STORM_LOG_ASSERT(writeIt == getRow(smallerRow).end(), "Unexpected position of write iterator.");
1020 // Update the row indications to account for the shift of indices at where the rows now start.
1021 if (!storm::utility::isZero(rowSizeDifference)) {
1022 for (index_type row = largerRow + 1; row <= smallerRow; ++row) {
1023 rowIndications[row] -= rowSizeDifference;
1025 }
1026 } else {
1027 auto writeIt = getRows(smallerRow, largerRow + 1).end() - 1;
1028
1029 // Write smaller row to its new position
1030 auto copyRow = getRow(smallerRow);
1031 for (auto smallerRowEntryIt = copyRow.end() - 1; smallerRowEntryIt != copyRow.begin() - 1; --smallerRowEntryIt) {
1032 *writeIt = std::move(*smallerRowEntryIt);
1033 --writeIt;
1034 }
1035
1036 // Write the intermediate rows into their correct position.
1037 if (!storm::utility::isZero(rowSizeDifference)) {
1038 for (auto intermediateRowEntryIt = getRows(smallerRow + 1, largerRow).end() - 1;
1039 intermediateRowEntryIt != getRows(smallerRow + 1, largerRow).begin() - 1; --intermediateRowEntryIt) {
1040 *writeIt = std::move(*intermediateRowEntryIt);
1041 --writeIt;
1043 } else {
1044 // skip the intermediate rows
1045 writeIt = getRow(smallerRow).end() - 1;
1046 }
1047
1048 // Write the larger row to its new position.
1049 for (auto largerRowEntryIt = largerRowContents.rbegin(); largerRowEntryIt != largerRowContents.rend(); ++largerRowEntryIt) {
1050 *writeIt = std::move(*largerRowEntryIt);
1051 --writeIt;
1052 }
1053
1054 STORM_LOG_ASSERT(writeIt == getRow(smallerRow).begin() - 1, "Unexpected position of write iterator.");
1055
1056 // Update row indications.
1057 // Update the row indications to account for the shift of indices at where the rows now start.
1058 if (!storm::utility::isZero(rowSizeDifference)) {
1059 for (index_type row = smallerRow + 1; row <= largerRow; ++row) {
1060 rowIndications[row] += rowSizeDifference;
1061 }
1062 }
1063 }
1064}
1066template<typename ValueType>
1067std::vector<ValueType> SparseMatrix<ValueType>::getRowSumVector() const {
1068 std::vector<ValueType> result(this->getRowCount());
1069
1070 index_type row = 0;
1071 for (auto resultIt = result.begin(), resultIte = result.end(); resultIt != resultIte; ++resultIt, ++row) {
1072 *resultIt = getRowSum(row);
1073 }
1075 return result;
1076}
1077
1078template<typename ValueType>
1080 ValueType result = storm::utility::zero<ValueType>();
1081 for (const_iterator it = this->begin(row), ite = this->end(row); it != ite; ++it) {
1082 if (constraint.get(it->getColumn())) {
1083 result += it->getValue();
1084 }
1085 }
1086 return result;
1087}
1088
1089template<typename ValueType>
1091 storm::storage::BitVector const& columnConstraint) const {
1092 std::vector<ValueType> result(rowConstraint.getNumberOfSetBits());
1093 index_type currentRowCount = 0;
1094 for (auto row : rowConstraint) {
1095 result[currentRowCount++] = getConstrainedRowSum(row, columnConstraint);
1096 }
1097 return result;
1098}
1100template<typename ValueType>
1102 storm::storage::BitVector const& columnConstraint) const {
1103 std::vector<ValueType> result;
1104 result.reserve(this->getNumRowsInRowGroups(rowGroupConstraint));
1105 if (!this->hasTrivialRowGrouping()) {
1106 for (auto rowGroup : rowGroupConstraint) {
1107 for (index_type row = this->getRowGroupIndices()[rowGroup]; row < this->getRowGroupIndices()[rowGroup + 1]; ++row) {
1108 result.push_back(getConstrainedRowSum(row, columnConstraint));
1109 }
1110 }
1111 } else {
1112 for (auto rowGroup : rowGroupConstraint) {
1113 result.push_back(getConstrainedRowSum(rowGroup, columnConstraint));
1114 }
1116 return result;
1117}
1118
1119template<typename ValueType>
1121 storm::storage::BitVector const& columnConstraint, bool insertDiagonalElements,
1122 storm::storage::BitVector const& makeZeroColumns) const {
1123 if (useGroups) {
1124 return getSubmatrix(rowConstraint, columnConstraint, this->getRowGroupIndices(), insertDiagonalElements, makeZeroColumns);
1125 } else {
1126 // Create a fake row grouping to reduce this to a call to a more general method.
1127 std::vector<index_type> fakeRowGroupIndices(rowCount + 1);
1128 index_type i = 0;
1129 for (std::vector<index_type>::iterator it = fakeRowGroupIndices.begin(); it != fakeRowGroupIndices.end(); ++it, ++i) {
1130 *it = i;
1132 auto res = getSubmatrix(rowConstraint, columnConstraint, fakeRowGroupIndices, insertDiagonalElements, makeZeroColumns);
1133
1134 // Create a new row grouping that reflects the new sizes of the row groups if the current matrix has a
1135 // non trivial row-grouping.
1136 if (!this->hasTrivialRowGrouping()) {
1137 std::vector<index_type> newRowGroupIndices;
1138 newRowGroupIndices.push_back(0);
1139 auto selectedRowIt = rowConstraint.begin();
1140
1141 // For this, we need to count how many rows were preserved in every group.
1142 for (index_type group = 0; group < this->getRowGroupCount(); ++group) {
1143 index_type newRowCount = 0;
1144 while (*selectedRowIt < this->getRowGroupIndices()[group + 1]) {
1145 ++selectedRowIt;
1146 ++newRowCount;
1148 if (newRowCount > 0) {
1149 newRowGroupIndices.push_back(newRowGroupIndices.back() + newRowCount);
1150 }
1151 }
1152
1153 res.trivialRowGrouping = false;
1154 res.rowGroupIndices = newRowGroupIndices;
1155 }
1156
1157 return res;
1158 }
1159}
1160
1161template<typename ValueType>
1163 storm::storage::BitVector const& columnConstraint, std::vector<index_type> const& rowGroupIndices,
1164 bool insertDiagonalEntries, storm::storage::BitVector const& makeZeroColumns) const {
1165 STORM_LOG_THROW(!rowGroupConstraint.empty() && !columnConstraint.empty(), storm::exceptions::InvalidArgumentException, "Cannot build empty submatrix.");
1166 index_type submatrixColumnCount = columnConstraint.getNumberOfSetBits();
1167
1168 // Start by creating a temporary vector that stores for each index whose bit is set to true the number of
1169 // bits that were set before that particular index.
1170 std::vector<index_type> columnBitsSetBeforeIndex = columnConstraint.getNumberOfSetBitsBeforeIndices();
1171 std::unique_ptr<std::vector<index_type>> tmp;
1172 if (rowGroupConstraint != columnConstraint) {
1173 tmp = std::make_unique<std::vector<index_type>>(rowGroupConstraint.getNumberOfSetBitsBeforeIndices());
1174 }
1175 std::vector<index_type> const& rowBitsSetBeforeIndex = tmp ? *tmp : columnBitsSetBeforeIndex;
1176
1177 // Then, we need to determine the number of entries and the number of rows of the submatrix.
1178 index_type subEntries = 0;
1179 index_type subRows = 0;
1180 index_type rowGroupCount = 0;
1181 for (auto index : rowGroupConstraint) {
1182 subRows += rowGroupIndices[index + 1] - rowGroupIndices[index];
1183 for (index_type i = rowGroupIndices[index]; i < rowGroupIndices[index + 1]; ++i) {
1184 bool foundDiagonalElement = false;
1185
1186 for (const_iterator it = this->begin(i), ite = this->end(i); it != ite; ++it) {
1187 if (columnConstraint.get(it->getColumn()) && (makeZeroColumns.size() == 0 || !makeZeroColumns.get(it->getColumn()))) {
1188 ++subEntries;
1189
1190 if (columnBitsSetBeforeIndex[it->getColumn()] == rowBitsSetBeforeIndex[index]) {
1191 foundDiagonalElement = true;
1192 }
1193 }
1194 }
1195
1196 // If requested, we need to reserve one entry more for inserting the diagonal zero entry.
1197 if (insertDiagonalEntries && !foundDiagonalElement && rowGroupCount < submatrixColumnCount) {
1198 ++subEntries;
1199 }
1200 }
1201 ++rowGroupCount;
1202 }
1203
1204 // Create and initialize resulting matrix.
1205 SparseMatrixBuilder<ValueType> matrixBuilder(subRows, submatrixColumnCount, subEntries, true, !this->hasTrivialRowGrouping());
1206
1207 // Copy over selected entries.
1208 rowGroupCount = 0;
1209 index_type rowCount = 0;
1210 subEntries = 0;
1211 for (auto index : rowGroupConstraint) {
1212 if (!this->hasTrivialRowGrouping()) {
1213 matrixBuilder.newRowGroup(rowCount);
1214 }
1215 for (index_type i = rowGroupIndices[index]; i < rowGroupIndices[index + 1]; ++i) {
1216 bool insertedDiagonalElement = false;
1217
1218 for (const_iterator it = this->begin(i), ite = this->end(i); it != ite; ++it) {
1219 if (columnConstraint.get(it->getColumn()) && (makeZeroColumns.size() == 0 || !makeZeroColumns.get(it->getColumn()))) {
1220 if (columnBitsSetBeforeIndex[it->getColumn()] == rowBitsSetBeforeIndex[index]) {
1221 insertedDiagonalElement = true;
1222 } else if (insertDiagonalEntries && !insertedDiagonalElement && columnBitsSetBeforeIndex[it->getColumn()] > rowBitsSetBeforeIndex[index]) {
1223 matrixBuilder.addNextValue(rowCount, rowGroupCount, storm::utility::zero<ValueType>());
1224 insertedDiagonalElement = true;
1225 }
1226 ++subEntries;
1227 matrixBuilder.addNextValue(rowCount, columnBitsSetBeforeIndex[it->getColumn()], it->getValue());
1228 }
1229 }
1230 if (insertDiagonalEntries && !insertedDiagonalElement && rowGroupCount < submatrixColumnCount) {
1231 matrixBuilder.addNextValue(rowCount, rowGroupCount, storm::utility::zero<ValueType>());
1232 }
1233 ++rowCount;
1234 }
1235 ++rowGroupCount;
1236 }
1237
1238 return matrixBuilder.build();
1239}
1240
1241template<typename ValueType>
1243 STORM_LOG_ASSERT(rowsToKeep.size() == this->getRowCount(), "Dimensions mismatch.");
1244
1245 // Count the number of entries of the resulting matrix
1246 index_type entryCount = 0;
1247 for (auto row : rowsToKeep) {
1248 entryCount += this->getRow(row).getNumberOfEntries();
1249 }
1250
1251 // Get the smallest row group index such that all row groups with at least this index are empty.
1252 index_type firstTrailingEmptyRowGroup = this->getRowGroupCount();
1253 for (auto groupIndexIt = this->getRowGroupIndices().rbegin() + 1; groupIndexIt != this->getRowGroupIndices().rend(); ++groupIndexIt) {
1254 if (rowsToKeep.getNextSetIndex(*groupIndexIt) != rowsToKeep.size()) {
1255 break;
1256 }
1257 --firstTrailingEmptyRowGroup;
1258 }
1259 STORM_LOG_THROW(allowEmptyRowGroups || firstTrailingEmptyRowGroup == this->getRowGroupCount(), storm::exceptions::InvalidArgumentException,
1260 "Empty rows are not allowed, but row group " << firstTrailingEmptyRowGroup << " is empty.");
1261
1262 // build the matrix. The row grouping will always be considered as nontrivial.
1263 SparseMatrixBuilder<ValueType> builder(rowsToKeep.getNumberOfSetBits(), this->getColumnCount(), entryCount, true, true, this->getRowGroupCount());
1264 index_type newRow = 0;
1265 for (index_type rowGroup = 0; rowGroup < firstTrailingEmptyRowGroup; ++rowGroup) {
1266 // Add a new row group
1267 builder.newRowGroup(newRow);
1268 bool rowGroupEmpty = true;
1269 for (index_type row = rowsToKeep.getNextSetIndex(this->getRowGroupIndices()[rowGroup]); row < this->getRowGroupIndices()[rowGroup + 1];
1270 row = rowsToKeep.getNextSetIndex(row + 1)) {
1271 rowGroupEmpty = false;
1272 for (auto const& entry : this->getRow(row)) {
1273 builder.addNextValue(newRow, entry.getColumn(), entry.getValue());
1274 }
1275 ++newRow;
1276 }
1277 STORM_LOG_THROW(allowEmptyRowGroups || !rowGroupEmpty, storm::exceptions::InvalidArgumentException,
1278 "Empty rows are not allowed, but row group " << rowGroup << " is empty.");
1279 }
1280
1281 // The all remaining row groups will be empty. Note that it is not allowed to call builder.addNewGroup(...) if there are no more rows afterwards.
1282 SparseMatrix<ValueType> res = builder.build();
1283 return res;
1284}
1285
1286template<typename ValueType>
1288 // Count the number of entries in the resulting matrix.
1289 index_type entryCount = 0;
1290 for (auto row : rowFilter) {
1291 entryCount += getRow(row).getNumberOfEntries();
1292 }
1293
1294 // Build the resulting matrix.
1296 for (auto row : rowFilter) {
1297 for (auto const& entry : getRow(row)) {
1298 builder.addNextValue(row, entry.getColumn(), entry.getValue());
1299 }
1300 }
1301 SparseMatrix<ValueType> result = builder.build();
1302
1303 // Add a row grouping if necessary.
1304 if (!hasTrivialRowGrouping()) {
1306 }
1307 return result;
1308}
1309
1310template<typename ValueType>
1315 for (index_type row = 0; row < getRowCount(); ++row) {
1316 for (auto const& entry : getRow(row)) {
1317 if (!storm::utility::isZero(entry.getValue())) {
1318 builder.addNextValue(row, entry.getColumn(), entry.getValue());
1319 }
1320 }
1321 }
1322 SparseMatrix<ValueType> result = builder.build();
1323 // Add a row grouping if necessary.
1324 if (!hasTrivialRowGrouping()) {
1326 }
1327 *this = std::move(result);
1328 }
1329}
1330
1331template<typename ValueType>
1332SparseMatrix<ValueType> SparseMatrix<ValueType>::selectRowsFromRowGroups(std::vector<index_type> const& rowGroupToRowIndexMapping,
1333 bool insertDiagonalEntries) const {
1334 // First, we need to count how many non-zero entries the resulting matrix will have and reserve space for
1335 // diagonal entries if requested.
1336 index_type subEntries = 0;
1337 for (index_type rowGroupIndex = 0, rowGroupIndexEnd = rowGroupToRowIndexMapping.size(); rowGroupIndex < rowGroupIndexEnd; ++rowGroupIndex) {
1338 // Determine which row we need to select from the current row group.
1339 STORM_LOG_ASSERT(rowGroupToRowIndexMapping[rowGroupIndex] < this->getRowGroupSize(rowGroupIndex),
1340 "Cannot point to row offset " << rowGroupToRowIndexMapping[rowGroupIndex] << " for rowGroup " << rowGroupIndex << " which starts at "
1341 << this->getRowGroupIndices()[rowGroupIndex] << " and ends at "
1342 << this->getRowGroupIndices()[rowGroupIndex + 1] << ".");
1343 index_type rowToCopy = this->getRowGroupIndices()[rowGroupIndex] + rowGroupToRowIndexMapping[rowGroupIndex];
1344
1345 // Iterate through that row and count the number of slots we have to reserve for copying.
1346 bool foundDiagonalElement = false;
1347 for (const_iterator it = this->begin(rowToCopy), ite = this->end(rowToCopy); it != ite; ++it) {
1348 if (it->getColumn() == rowGroupIndex) {
1349 foundDiagonalElement = true;
1350 }
1351 ++subEntries;
1352 }
1353 if (insertDiagonalEntries && !foundDiagonalElement) {
1354 ++subEntries;
1355 }
1356 }
1357
1358 // Now create the matrix to be returned with the appropriate size.
1359 SparseMatrixBuilder<ValueType> matrixBuilder(rowGroupIndices.get().size() - 1, columnCount, subEntries);
1360
1361 // Copy over the selected lines from the source matrix.
1362 for (index_type rowGroupIndex = 0, rowGroupIndexEnd = rowGroupToRowIndexMapping.size(); rowGroupIndex < rowGroupIndexEnd; ++rowGroupIndex) {
1363 // Determine which row we need to select from the current row group.
1364 index_type rowToCopy = this->getRowGroupIndices()[rowGroupIndex] + rowGroupToRowIndexMapping[rowGroupIndex];
1365
1366 // Iterate through that row and copy the entries. This also inserts a zero element on the diagonal if
1367 // there is no entry yet.
1368 bool insertedDiagonalElement = false;
1369 for (const_iterator it = this->begin(rowToCopy), ite = this->end(rowToCopy); it != ite; ++it) {
1370 if (it->getColumn() == rowGroupIndex) {
1371 insertedDiagonalElement = true;
1372 } else if (insertDiagonalEntries && !insertedDiagonalElement && it->getColumn() > rowGroupIndex) {
1373 matrixBuilder.addNextValue(rowGroupIndex, rowGroupIndex, storm::utility::zero<ValueType>());
1374 insertedDiagonalElement = true;
1375 }
1376 matrixBuilder.addNextValue(rowGroupIndex, it->getColumn(), it->getValue());
1377 }
1378 if (insertDiagonalEntries && !insertedDiagonalElement) {
1379 matrixBuilder.addNextValue(rowGroupIndex, rowGroupIndex, storm::utility::zero<ValueType>());
1380 }
1381 }
1382
1383 // Finalize created matrix and return result.
1384 return matrixBuilder.build();
1385}
1386
1387template<typename ValueType>
1389 bool insertDiagonalEntries) const {
1390 // First, we need to count how many non-zero entries the resulting matrix will have and reserve space for
1391 // diagonal entries if requested.
1392 index_type newEntries = 0;
1393 for (index_type row = 0, rowEnd = rowIndexSequence.size(); row < rowEnd; ++row) {
1394 bool foundDiagonalElement = false;
1395 for (const_iterator it = this->begin(rowIndexSequence[row]), ite = this->end(rowIndexSequence[row]); it != ite; ++it) {
1396 if (it->getColumn() == row) {
1397 foundDiagonalElement = true;
1398 }
1399 ++newEntries;
1400 }
1401 if (insertDiagonalEntries && !foundDiagonalElement) {
1402 ++newEntries;
1403 }
1404 }
1405
1406 // Now create the matrix to be returned with the appropriate size.
1407 SparseMatrixBuilder<ValueType> matrixBuilder(rowIndexSequence.size(), columnCount, newEntries);
1408
1409 // Copy over the selected rows from the source matrix.
1410 for (index_type row = 0, rowEnd = rowIndexSequence.size(); row < rowEnd; ++row) {
1411 bool insertedDiagonalElement = false;
1412 for (const_iterator it = this->begin(rowIndexSequence[row]), ite = this->end(rowIndexSequence[row]); it != ite; ++it) {
1413 if (it->getColumn() == row) {
1414 insertedDiagonalElement = true;
1415 } else if (insertDiagonalEntries && !insertedDiagonalElement && it->getColumn() > row) {
1416 matrixBuilder.addNextValue(row, row, storm::utility::zero<ValueType>());
1417 insertedDiagonalElement = true;
1418 }
1419 matrixBuilder.addNextValue(row, it->getColumn(), it->getValue());
1420 }
1421 if (insertDiagonalEntries && !insertedDiagonalElement) {
1422 matrixBuilder.addNextValue(row, row, storm::utility::zero<ValueType>());
1423 }
1424 }
1425
1426 // Finally create matrix and return result.
1427 return matrixBuilder.build();
1428}
1429
1430template<typename ValueType>
1431SparseMatrix<ValueType> SparseMatrix<ValueType>::permuteRows(std::vector<index_type> const& inversePermutation) const {
1432 // Now create the matrix to be returned with the appropriate size.
1433 // The entry size is only adequate if this is indeed a permutation.
1434 SparseMatrixBuilder<ValueType> matrixBuilder(inversePermutation.size(), columnCount, entryCount);
1435
1436 // Copy over the selected rows from the source matrix.
1437
1438 for (index_type writeTo = 0; writeTo < inversePermutation.size(); ++writeTo) {
1439 index_type const& readFrom = inversePermutation[writeTo];
1440 auto row = this->getRow(readFrom);
1441 for (auto const& entry : row) {
1442 matrixBuilder.addNextValue(writeTo, entry.getColumn(), entry.getValue());
1443 }
1444 }
1445 // Finally create matrix and return result.
1446 auto result = matrixBuilder.build();
1447 if (this->rowGroupIndices) {
1448 result.setRowGroupIndices(this->rowGroupIndices.get());
1449 }
1450 return result;
1451}
1452
1453template<typename ValueType>
1454SparseMatrix<ValueType> SparseMatrix<ValueType>::permuteRowGroupsAndColumns(std::vector<index_type> const& inverseRowGroupPermutation,
1455 std::vector<index_type> const& columnPermutation) const {
1456 STORM_LOG_ASSERT(storm::utility::permutation::isValidPermutation(inverseRowGroupPermutation), "Row group permutation is not a permutation.");
1457 STORM_LOG_ASSERT(storm::utility::permutation::isValidPermutation(columnPermutation), "Column permutation is not a permutation.");
1458 index_type const rowCount = getRowCount();
1460 auto oldGroupIt = inverseRowGroupPermutation.cbegin();
1461 index_type newRowIndex = 0;
1462 while (newRowIndex < rowCount) {
1463 if (!hasTrivialRowGrouping()) {
1464 matrixBuilder.newRowGroup(newRowIndex);
1465 }
1466 for (auto oldRowIndex : getRowGroupIndices(*oldGroupIt)) {
1467 for (auto const& oldEntry : getRow(oldRowIndex)) {
1468 matrixBuilder.addNextValue(newRowIndex, columnPermutation[oldEntry.getColumn()], oldEntry.getValue());
1469 }
1470 ++newRowIndex;
1471 }
1472 ++oldGroupIt;
1473 }
1474 return matrixBuilder.build();
1475}
1476
1477template<typename ValueType>
1478SparseMatrix<ValueType> SparseMatrix<ValueType>::transpose(bool joinGroups, bool keepZeros) const {
1479 index_type rowCount = this->getColumnCount();
1480 index_type columnCount = joinGroups ? this->getRowGroupCount() : this->getRowCount();
1481 index_type entryCount;
1482 if (keepZeros) {
1483 entryCount = this->getEntryCount();
1484 } else {
1486 entryCount = this->getNonzeroEntryCount();
1487 }
1488
1489 std::vector<index_type> rowIndications(rowCount + 1);
1490 std::vector<MatrixEntry<index_type, ValueType>> columnsAndValues(entryCount);
1491
1492 // First, we need to count how many entries each column has.
1493 for (index_type group = 0; group < columnCount; ++group) {
1494 for (auto const& transition : joinGroups ? this->getRowGroup(group) : this->getRow(group)) {
1495 if (transition.getValue() != storm::utility::zero<ValueType>() || keepZeros) {
1496 ++rowIndications[transition.getColumn() + 1];
1497 }
1498 }
1499 }
1500
1501 // Now compute the accumulated offsets.
1502 for (index_type i = 1; i < rowCount + 1; ++i) {
1503 rowIndications[i] = rowIndications[i - 1] + rowIndications[i];
1504 }
1505
1506 // Create an array that stores the index for the next value to be added for
1507 // each row in the transposed matrix. Initially this corresponds to the previously
1508 // computed accumulated offsets.
1509 std::vector<index_type> nextIndices = rowIndications;
1510
1511 // Now we are ready to actually fill in the values of the transposed matrix.
1512 for (index_type group = 0; group < columnCount; ++group) {
1513 for (auto const& transition : joinGroups ? this->getRowGroup(group) : this->getRow(group)) {
1514 if (transition.getValue() != storm::utility::zero<ValueType>() || keepZeros) {
1515 columnsAndValues[nextIndices[transition.getColumn()]] = std::make_pair(group, transition.getValue());
1516 nextIndices[transition.getColumn()]++;
1517 }
1518 }
1519 }
1520
1521 storm::storage::SparseMatrix<ValueType> transposedMatrix(columnCount, std::move(rowIndications), std::move(columnsAndValues), boost::none);
1522
1523 return transposedMatrix;
1524}
1525
1526template<typename ValueType>
1527SparseMatrix<ValueType> SparseMatrix<ValueType>::transposeSelectedRowsFromRowGroups(std::vector<uint64_t> const& rowGroupChoices, bool keepZeros) const {
1528 index_type rowCount = this->getColumnCount();
1529 index_type columnCount = this->getRowGroupCount();
1530
1531 // Get the overall entry count as well as the number of entries of each column
1532 index_type entryCount = 0;
1533 std::vector<index_type> rowIndications(columnCount + 1);
1534 auto rowGroupChoiceIt = rowGroupChoices.begin();
1535 for (index_type rowGroup = 0; rowGroup < columnCount; ++rowGroup, ++rowGroupChoiceIt) {
1536 for (auto const& entry : this->getRow(rowGroup, *rowGroupChoiceIt)) {
1537 if (keepZeros || !storm::utility::isZero(entry.getValue())) {
1538 ++entryCount;
1539 ++rowIndications[entry.getColumn() + 1];
1540 }
1541 }
1542 }
1543
1544 // Now compute the accumulated offsets.
1545 for (index_type i = 1; i < rowCount + 1; ++i) {
1546 rowIndications[i] = rowIndications[i - 1] + rowIndications[i];
1547 }
1548
1549 std::vector<MatrixEntry<index_type, ValueType>> columnsAndValues(entryCount);
1550
1551 // Create an array that stores the index for the next value to be added for
1552 // each row in the transposed matrix. Initially this corresponds to the previously
1553 // computed accumulated offsets.
1554 std::vector<index_type> nextIndices = rowIndications;
1555
1556 // Now we are ready to actually fill in the values of the transposed matrix.
1557 rowGroupChoiceIt = rowGroupChoices.begin();
1558 for (index_type rowGroup = 0; rowGroup < columnCount; ++rowGroup, ++rowGroupChoiceIt) {
1559 for (auto const& entry : this->getRow(rowGroup, *rowGroupChoiceIt)) {
1560 if (keepZeros || !storm::utility::isZero(entry.getValue())) {
1561 columnsAndValues[nextIndices[entry.getColumn()]] = std::make_pair(rowGroup, entry.getValue());
1562 ++nextIndices[entry.getColumn()];
1563 }
1564 }
1565 }
1566
1567 return storm::storage::SparseMatrix<ValueType>(std::move(columnCount), std::move(rowIndications), std::move(columnsAndValues), boost::none);
1568}
1569
1570template<typename ValueType>
1575
1576template<typename ValueType>
1578 // Now iterate over all row groups and set the diagonal elements to the inverted value.
1579 // If there is a row without the diagonal element, an exception is thrown.
1580 ValueType one = storm::utility::one<ValueType>();
1581 ValueType zero = storm::utility::zero<ValueType>();
1582 bool foundDiagonalElement = false;
1583 for (index_type group = 0; group < this->getRowGroupCount(); ++group) {
1584 for (auto& entry : this->getRowGroup(group)) {
1585 if (entry.getColumn() == group) {
1586 if (entry.getValue() == one) {
1587 --this->nonzeroEntryCount;
1588 entry.setValue(zero);
1589 } else if (entry.getValue() == zero) {
1590 ++this->nonzeroEntryCount;
1591 entry.setValue(one);
1592 } else {
1593 entry.setValue(one - entry.getValue());
1594 }
1595 foundDiagonalElement = true;
1596 }
1597 }
1598
1599 // Throw an exception if a row did not have an element on the diagonal.
1600 if (!foundDiagonalElement) {
1601 throw storm::exceptions::InvalidArgumentException() << "Illegal call to SparseMatrix::invertDiagonal: matrix is missing diagonal entries.";
1602 }
1603 }
1604}
1605
1606template<typename ValueType>
1608 // Iterate over all row groups and negate all the elements that are not on the diagonal.
1609 for (index_type group = 0; group < this->getRowGroupCount(); ++group) {
1610 for (auto& entry : this->getRowGroup(group)) {
1611 if (entry.getColumn() != group) {
1612 entry.setValue(-entry.getValue());
1613 }
1614 }
1615 }
1616}
1617
1618template<typename ValueType>
1620 // Iterate over all rows and negate all the elements that are not on the diagonal.
1621 for (index_type group = 0; group < this->getRowGroupCount(); ++group) {
1622 for (auto& entry : this->getRowGroup(group)) {
1623 if (entry.getColumn() == group) {
1624 --this->nonzeroEntryCount;
1625 entry.setValue(storm::utility::zero<ValueType>());
1626 }
1627 }
1628 }
1629 if (dropZeroEntries) {
1630 this->dropZeroEntries();
1631 }
1632}
1633
1634template<typename ValueType>
1635typename std::pair<storm::storage::SparseMatrix<ValueType>, std::vector<ValueType>> SparseMatrix<ValueType>::getJacobiDecomposition() const {
1636 STORM_LOG_THROW(this->getRowCount() == this->getColumnCount(), storm::exceptions::InvalidArgumentException,
1637 "Canno compute Jacobi decomposition of non-square matrix.");
1638
1639 // Prepare the resulting data structures.
1640 SparseMatrixBuilder<ValueType> luBuilder(this->getRowCount(), this->getColumnCount());
1641 std::vector<ValueType> invertedDiagonal(rowCount);
1642
1643 // Copy entries to the appropriate matrices.
1644 for (index_type rowNumber = 0; rowNumber < rowCount; ++rowNumber) {
1645 for (const_iterator it = this->begin(rowNumber), ite = this->end(rowNumber); it != ite; ++it) {
1646 if (it->getColumn() == rowNumber) {
1647 invertedDiagonal[rowNumber] = storm::utility::one<ValueType>() / it->getValue();
1648 } else {
1649 luBuilder.addNextValue(rowNumber, it->getColumn(), it->getValue());
1650 }
1651 }
1652 }
1653
1654 return std::make_pair(luBuilder.build(), std::move(invertedDiagonal));
1655}
1656
1657#ifdef STORM_HAVE_CARL
1658template<>
1659typename std::pair<storm::storage::SparseMatrix<Interval>, std::vector<Interval>> SparseMatrix<Interval>::getJacobiDecomposition() const {
1660 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This operation is not supported.");
1661}
1662
1663template<>
1664typename std::pair<storm::storage::SparseMatrix<RationalFunction>, std::vector<RationalFunction>> SparseMatrix<RationalFunction>::getJacobiDecomposition()
1665 const {
1666 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This operation is not supported.");
1667}
1668#endif
1669
1670template<typename ValueType>
1671template<typename OtherValueType, typename ResultValueType>
1673 index_type const& row) const {
1678
1679 ResultValueType result = storm::utility::zero<ResultValueType>();
1680 for (; it1 != ite1 && it2 != ite2; ++it1) {
1681 if (it1->getColumn() < it2->getColumn()) {
1682 continue;
1683 } else {
1684 // If the precondition of this method (i.e. that the given matrix is a submatrix
1685 // of the current one) was fulfilled, we know now that the two elements are in
1686 // the same column, so we can multiply and add them to the row sum vector.
1687 STORM_LOG_ASSERT(it1->getColumn() == it2->getColumn(), "The given matrix is not a submatrix of this one.");
1688 result += it2->getValue() * OtherValueType(it1->getValue());
1689 ++it2;
1690 }
1691 }
1692 return result;
1693}
1694
1695template<typename ValueType>
1696template<typename OtherValueType, typename ResultValueType>
1698 std::vector<ResultValueType> result;
1699 result.reserve(rowCount);
1700 for (index_type row = 0; row < rowCount && row < otherMatrix.getRowCount(); ++row) {
1701 result.push_back(getPointwiseProductRowSum<OtherValueType, ResultValueType>(otherMatrix, row));
1702 }
1703 return result;
1704}
1705
1706template<typename ValueType>
1707void SparseMatrix<ValueType>::multiplyWithVector(std::vector<ValueType> const& vector, std::vector<ValueType>& result,
1708 std::vector<value_type> const* summand) const {
1709 // If the vector and the result are aliases and this is not set to be allowed, we need and temporary vector.
1710 std::vector<ValueType>* target;
1711 std::vector<ValueType> temporary;
1712 if (&vector == &result) {
1713 STORM_LOG_WARN("Vectors are aliased. Using temporary, which is potentially slow.");
1714 temporary = std::vector<ValueType>(vector.size());
1715 target = &temporary;
1716 } else {
1717 target = &result;
1718 }
1719
1720 this->multiplyWithVectorForward(vector, *target, summand);
1721
1722 if (target == &temporary) {
1723 std::swap(result, *target);
1724 }
1725}
1726
1727template<typename ValueType>
1728void SparseMatrix<ValueType>::multiplyWithVectorForward(std::vector<ValueType> const& vector, std::vector<ValueType>& result,
1729 std::vector<value_type> const* summand) const {
1730 const_iterator it = this->begin();
1731 const_iterator ite;
1732 std::vector<index_type>::const_iterator rowIterator = rowIndications.begin();
1733 typename std::vector<ValueType>::iterator resultIterator = result.begin();
1734 typename std::vector<ValueType>::iterator resultIteratorEnd = result.end();
1735 typename std::vector<ValueType>::const_iterator summandIterator;
1736 if (summand) {
1737 summandIterator = summand->begin();
1738 }
1739
1740 for (; resultIterator != resultIteratorEnd; ++rowIterator, ++resultIterator, ++summandIterator) {
1741 ValueType newValue;
1742 if (summand) {
1743 newValue = *summandIterator;
1744 } else {
1745 newValue = storm::utility::zero<ValueType>();
1746 }
1747
1748 for (ite = this->begin() + *(rowIterator + 1); it != ite; ++it) {
1749 newValue += it->getValue() * vector[it->getColumn()];
1750 }
1751
1752 *resultIterator = newValue;
1753 }
1754}
1755
1756template<typename ValueType>
1757void SparseMatrix<ValueType>::multiplyWithVectorBackward(std::vector<ValueType> const& vector, std::vector<ValueType>& result,
1758 std::vector<value_type> const* summand) const {
1759 const_iterator it = this->end() - 1;
1760 const_iterator ite;
1761 std::vector<index_type>::const_iterator rowIterator = rowIndications.end() - 2;
1762 typename std::vector<ValueType>::iterator resultIterator = result.end() - 1;
1763 typename std::vector<ValueType>::iterator resultIteratorEnd = result.begin() - 1;
1764 typename std::vector<ValueType>::const_iterator summandIterator;
1765 if (summand) {
1766 summandIterator = summand->end() - 1;
1767 }
1768
1769 for (; resultIterator != resultIteratorEnd; --rowIterator, --resultIterator, --summandIterator) {
1770 ValueType newValue;
1771 if (summand) {
1772 newValue = *summandIterator;
1773 } else {
1774 newValue = storm::utility::zero<ValueType>();
1775 }
1776
1777 for (ite = this->begin() + *rowIterator - 1; it != ite; --it) {
1778 newValue += (it->getValue() * vector[it->getColumn()]);
1779 }
1780
1781 *resultIterator = newValue;
1782 }
1783}
1784
1785template<typename ValueType>
1786ValueType SparseMatrix<ValueType>::multiplyRowWithVector(index_type row, std::vector<ValueType> const& vector) const {
1787 ValueType result = storm::utility::zero<ValueType>();
1788
1789 for (auto const& entry : this->getRow(row)) {
1790 result += entry.getValue() * vector[entry.getColumn()];
1791 }
1792 return result;
1793}
1794
1795template<typename ValueType>
1796void SparseMatrix<ValueType>::performSuccessiveOverRelaxationStep(ValueType omega, std::vector<ValueType>& x, std::vector<ValueType> const& b) const {
1797 const_iterator it = this->end() - 1;
1798 const_iterator ite;
1799 std::vector<index_type>::const_iterator rowIterator = rowIndications.end() - 2;
1800 typename std::vector<ValueType>::const_iterator bIt = b.end() - 1;
1801 typename std::vector<ValueType>::iterator resultIterator = x.end() - 1;
1802 typename std::vector<ValueType>::iterator resultIteratorEnd = x.begin() - 1;
1803
1804 index_type currentRow = getRowCount();
1805 for (; resultIterator != resultIteratorEnd; --rowIterator, --resultIterator, --bIt) {
1806 --currentRow;
1807 ValueType tmpValue = storm::utility::zero<ValueType>();
1808 ValueType diagonalElement = storm::utility::zero<ValueType>();
1809
1810 for (ite = this->begin() + *rowIterator - 1; it != ite; --it) {
1811 if (it->getColumn() != currentRow) {
1812 tmpValue += it->getValue() * x[it->getColumn()];
1813 } else {
1814 diagonalElement += it->getValue();
1815 }
1816 }
1817 assert(!storm::utility::isZero(diagonalElement));
1818 *resultIterator = ((storm::utility::one<ValueType>() - omega) * *resultIterator) + (omega / diagonalElement) * (*bIt - tmpValue);
1819 }
1820}
1821
1822#ifdef STORM_HAVE_CARL
1823template<>
1824void SparseMatrix<Interval>::performSuccessiveOverRelaxationStep(Interval, std::vector<Interval>&, std::vector<Interval> const&) const {
1825 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "This operation is not supported.");
1826}
1827#endif
1828
1829template<typename ValueType>
1830void SparseMatrix<ValueType>::performWalkerChaeStep(std::vector<ValueType> const& x, std::vector<ValueType> const& columnSums, std::vector<ValueType> const& b,
1831 std::vector<ValueType> const& ax, std::vector<ValueType>& result) const {
1832 const_iterator it = this->begin();
1833 const_iterator ite;
1834 std::vector<index_type>::const_iterator rowIterator = rowIndications.begin();
1835
1836 // Clear all previous entries.
1837 ValueType zero = storm::utility::zero<ValueType>();
1838 for (auto& entry : result) {
1839 entry = zero;
1840 }
1841
1842 for (index_type row = 0; row < rowCount; ++row, ++rowIterator) {
1843 for (ite = this->begin() + *(rowIterator + 1); it != ite; ++it) {
1844 result[it->getColumn()] += it->getValue() * (b[row] / ax[row]);
1845 }
1846 }
1847
1848 auto xIterator = x.begin();
1849 auto sumsIterator = columnSums.begin();
1850 for (auto& entry : result) {
1851 entry *= *xIterator / *sumsIterator;
1852 ++xIterator;
1853 ++sumsIterator;
1854 }
1855}
1856
1857template<>
1858void SparseMatrix<Interval>::performWalkerChaeStep(std::vector<Interval> const& x, std::vector<Interval> const& rowSums, std::vector<Interval> const& b,
1859 std::vector<Interval> const& ax, std::vector<Interval>& result) const {
1860 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "This operation is not supported.");
1861}
1862
1863template<typename ValueType>
1864void SparseMatrix<ValueType>::multiplyAndReduceForward(OptimizationDirection const& dir, std::vector<uint64_t> const& rowGroupIndices,
1865 std::vector<ValueType> const& vector, std::vector<ValueType> const* summand,
1866 std::vector<ValueType>& result, std::vector<uint64_t>* choices) const {
1867 if (dir == OptimizationDirection::Minimize) {
1868 multiplyAndReduceForward<storm::utility::ElementLess<ValueType>>(rowGroupIndices, vector, summand, result, choices);
1869 } else {
1870 multiplyAndReduceForward<storm::utility::ElementGreater<ValueType>>(rowGroupIndices, vector, summand, result, choices);
1871 }
1872}
1873
1874template<typename ValueType>
1875template<typename Compare>
1876void SparseMatrix<ValueType>::multiplyAndReduceForward(std::vector<uint64_t> const& rowGroupIndices, std::vector<ValueType> const& vector,
1877 std::vector<ValueType> const* summand, std::vector<ValueType>& result,
1878 std::vector<uint64_t>* choices) const {
1879 Compare compare;
1880 auto elementIt = this->begin();
1881 auto rowGroupIt = rowGroupIndices.begin();
1882 auto rowIt = rowIndications.begin();
1883 typename std::vector<ValueType>::const_iterator summandIt;
1884 if (summand) {
1885 summandIt = summand->begin();
1886 }
1887 typename std::vector<uint64_t>::iterator choiceIt;
1888 if (choices) {
1889 choiceIt = choices->begin();
1890 }
1891
1892 // Variables for correctly tracking choices (only update if new choice is strictly better).
1893 ValueType oldSelectedChoiceValue;
1894 uint64_t selectedChoice;
1895
1896 uint64_t currentRow = 0;
1897 for (auto resultIt = result.begin(), resultIte = result.end(); resultIt != resultIte; ++resultIt, ++choiceIt, ++rowGroupIt) {
1898 ValueType currentValue = storm::utility::zero<ValueType>();
1899
1900 // Only multiply and reduce if there is at least one row in the group.
1901 if (*rowGroupIt < *(rowGroupIt + 1)) {
1902 if (summand) {
1903 currentValue = *summandIt;
1904 ++summandIt;
1905 }
1906
1907 for (auto elementIte = this->begin() + *(rowIt + 1); elementIt != elementIte; ++elementIt) {
1908 currentValue += elementIt->getValue() * vector[elementIt->getColumn()];
1909 }
1910
1911 if (choices) {
1912 selectedChoice = 0;
1913 if (*choiceIt == 0) {
1914 oldSelectedChoiceValue = currentValue;
1915 }
1916 }
1917
1918 ++rowIt;
1919 ++currentRow;
1920
1921 for (; currentRow < *(rowGroupIt + 1); ++rowIt, ++currentRow) {
1922 ValueType newValue = summand ? *summandIt : storm::utility::zero<ValueType>();
1923 for (auto elementIte = this->begin() + *(rowIt + 1); elementIt != elementIte; ++elementIt) {
1924 newValue += elementIt->getValue() * vector[elementIt->getColumn()];
1925 }
1926
1927 if (choices && currentRow == *choiceIt + *rowGroupIt) {
1928 oldSelectedChoiceValue = newValue;
1929 }
1930
1931 if (compare(newValue, currentValue)) {
1932 currentValue = newValue;
1933 if (choices) {
1934 selectedChoice = currentRow - *rowGroupIt;
1935 }
1936 }
1937 if (summand) {
1938 ++summandIt;
1939 }
1940 }
1941
1942 // Finally write value to target vector.
1943 *resultIt = currentValue;
1944 if (choices && compare(currentValue, oldSelectedChoiceValue)) {
1945 *choiceIt = selectedChoice;
1946 }
1947 }
1948 }
1949}
1950
1951template<>
1952void SparseMatrix<storm::RationalFunction>::multiplyAndReduceForward(OptimizationDirection const& dir, std::vector<uint64_t> const& rowGroupIndices,
1953 std::vector<storm::RationalFunction> const& vector,
1954 std::vector<storm::RationalFunction> const* b,
1955 std::vector<storm::RationalFunction>& result, std::vector<uint64_t>* choices) const {
1956 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "This operation is not supported.");
1957}
1958
1959template<typename ValueType>
1960void SparseMatrix<ValueType>::multiplyAndReduceBackward(OptimizationDirection const& dir, std::vector<uint64_t> const& rowGroupIndices,
1961 std::vector<ValueType> const& vector, std::vector<ValueType> const* summand,
1962 std::vector<ValueType>& result, std::vector<uint64_t>* choices) const {
1963 if (dir == storm::OptimizationDirection::Minimize) {
1964 multiplyAndReduceBackward<storm::utility::ElementLess<ValueType>>(rowGroupIndices, vector, summand, result, choices);
1965 } else {
1966 multiplyAndReduceBackward<storm::utility::ElementGreater<ValueType>>(rowGroupIndices, vector, summand, result, choices);
1967 }
1968}
1969
1970template<typename ValueType>
1971template<typename Compare>
1972void SparseMatrix<ValueType>::multiplyAndReduceBackward(std::vector<uint64_t> const& rowGroupIndices, std::vector<ValueType> const& vector,
1973 std::vector<ValueType> const* summand, std::vector<ValueType>& result,
1974 std::vector<uint64_t>* choices) const {
1975 Compare compare;
1976 auto elementIt = this->end() - 1;
1977 auto rowGroupIt = rowGroupIndices.end() - 2;
1978 auto rowIt = rowIndications.end() - 2;
1979 typename std::vector<ValueType>::const_iterator summandIt;
1980 if (summand) {
1981 summandIt = summand->end() - 1;
1982 }
1983 typename std::vector<uint64_t>::iterator choiceIt;
1984 if (choices) {
1985 choiceIt = choices->end() - 1;
1986 }
1987
1988 // Variables for correctly tracking choices (only update if new choice is strictly better).
1989 ValueType oldSelectedChoiceValue;
1990 uint64_t selectedChoice;
1991
1992 uint64_t currentRow = this->getRowCount() - 1;
1993 for (auto resultIt = result.end() - 1, resultIte = result.begin() - 1; resultIt != resultIte; --resultIt, --choiceIt, --rowGroupIt) {
1994 ValueType currentValue = storm::utility::zero<ValueType>();
1995
1996 // Only multiply and reduce if there is at least one row in the group.
1997 if (*rowGroupIt < *(rowGroupIt + 1)) {
1998 if (summand) {
1999 currentValue = *summandIt;
2000 --summandIt;
2001 }
2002
2003 for (auto elementIte = this->begin() + *rowIt - 1; elementIt != elementIte; --elementIt) {
2004 currentValue += elementIt->getValue() * vector[elementIt->getColumn()];
2005 }
2006 if (choices) {
2007 selectedChoice = currentRow - *rowGroupIt;
2008 if (*choiceIt == selectedChoice) {
2009 oldSelectedChoiceValue = currentValue;
2010 }
2011 }
2012 --rowIt;
2013 --currentRow;
2014
2015 for (uint64_t i = *rowGroupIt + 1, end = *(rowGroupIt + 1); i < end; --rowIt, --currentRow, ++i, --summandIt) {
2016 ValueType newValue = summand ? *summandIt : storm::utility::zero<ValueType>();
2017 for (auto elementIte = this->begin() + *rowIt - 1; elementIt != elementIte; --elementIt) {
2018 newValue += elementIt->getValue() * vector[elementIt->getColumn()];
2019 }
2020
2021 if (choices && currentRow == *choiceIt + *rowGroupIt) {
2022 oldSelectedChoiceValue = newValue;
2023 }
2024
2025 if (compare(newValue, currentValue)) {
2026 currentValue = newValue;
2027 if (choices) {
2028 selectedChoice = currentRow - *rowGroupIt;
2029 }
2030 }
2031 }
2032
2033 // Finally write value to target vector.
2034 *resultIt = currentValue;
2035 if (choices && compare(currentValue, oldSelectedChoiceValue)) {
2036 *choiceIt = selectedChoice;
2037 }
2038 }
2039 }
2040}
2041
2042template<>
2043void SparseMatrix<storm::RationalFunction>::multiplyAndReduceBackward(OptimizationDirection const& dir, std::vector<uint64_t> const& rowGroupIndices,
2044 std::vector<storm::RationalFunction> const& vector,
2045 std::vector<storm::RationalFunction> const* b,
2046 std::vector<storm::RationalFunction>& result, std::vector<uint64_t>* choices) const {
2047 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "This operation is not supported.");
2048}
2049
2050template<typename ValueType>
2051void SparseMatrix<ValueType>::multiplyAndReduce(OptimizationDirection const& dir, std::vector<uint64_t> const& rowGroupIndices,
2052 std::vector<ValueType> const& vector, std::vector<ValueType> const* summand, std::vector<ValueType>& result,
2053 std::vector<uint64_t>* choices) const {
2054 // If the vector and the result are aliases, we need and temporary vector.
2055 std::vector<ValueType>* target;
2056 std::vector<ValueType> temporary;
2057 if (&vector == &result) {
2058 STORM_LOG_WARN("Vectors are aliased but are not allowed to be. Using temporary, which is potentially slow.");
2059 temporary = std::vector<ValueType>(vector.size());
2060 target = &temporary;
2061 } else {
2062 target = &result;
2063 }
2064
2065 this->multiplyAndReduceForward(dir, rowGroupIndices, vector, summand, *target, choices);
2066
2067 if (target == &temporary) {
2068 std::swap(temporary, result);
2069 }
2070}
2071
2072template<typename ValueType>
2073void SparseMatrix<ValueType>::multiplyVectorWithMatrix(std::vector<value_type> const& vector, std::vector<value_type>& result) const {
2074 const_iterator it = this->begin();
2075 const_iterator ite;
2076 std::vector<index_type>::const_iterator rowIterator = rowIndications.begin();
2077 std::vector<index_type>::const_iterator rowIteratorEnd = rowIndications.end();
2078
2079 index_type currentRow = 0;
2080 for (; rowIterator != rowIteratorEnd - 1; ++rowIterator) {
2081 for (ite = this->begin() + *(rowIterator + 1); it != ite; ++it) {
2082 result[it->getColumn()] += it->getValue() * vector[currentRow];
2083 }
2084 ++currentRow;
2085 }
2086}
2087
2088template<typename ValueType>
2089void SparseMatrix<ValueType>::scaleRowsInPlace(std::vector<ValueType> const& factors) {
2090 STORM_LOG_ASSERT(factors.size() == this->getRowCount(), "Can not scale rows: Number of rows and number of scaling factors do not match.");
2091 index_type row = 0;
2092 for (auto const& factor : factors) {
2093 for (auto& entry : getRow(row)) {
2094 entry.setValue(entry.getValue() * factor);
2095 }
2096 ++row;
2097 }
2098}
2099
2100template<typename ValueType>
2101void SparseMatrix<ValueType>::divideRowsInPlace(std::vector<ValueType> const& divisors) {
2102 STORM_LOG_ASSERT(divisors.size() == this->getRowCount(), "Can not divide rows: Number of rows and number of divisors do not match.");
2103 index_type row = 0;
2104 for (auto const& divisor : divisors) {
2105 STORM_LOG_ASSERT(!storm::utility::isZero(divisor), "Can not divide row " << row << " by 0.");
2106 for (auto& entry : getRow(row)) {
2107 entry.setValue(entry.getValue() / divisor);
2108 }
2109 ++row;
2110 }
2111}
2112
2113#ifdef STORM_HAVE_CARL
2114template<>
2115void SparseMatrix<Interval>::divideRowsInPlace(std::vector<Interval> const&) {
2116 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This operation is not supported.");
2117}
2118#endif
2119
2120template<typename ValueType>
2122 return const_rows(this->columnsAndValues.begin() + this->rowIndications[startRow], this->rowIndications[endRow] - this->rowIndications[startRow]);
2123}
2124
2125template<typename ValueType>
2127 return rows(this->columnsAndValues.begin() + this->rowIndications[startRow], this->rowIndications[endRow] - this->rowIndications[startRow]);
2128}
2129
2130template<typename ValueType>
2132 return getRows(row, row + 1);
2133}
2134
2135template<typename ValueType>
2139
2140template<typename ValueType>
2142 STORM_LOG_ASSERT(rowGroup < this->getRowGroupCount(), "Row group is out-of-bounds.");
2143 STORM_LOG_ASSERT(offset < this->getRowGroupSize(rowGroup), "Row offset in row-group is out-of-bounds.");
2144 if (!this->hasTrivialRowGrouping()) {
2145 return getRow(this->getRowGroupIndices()[rowGroup] + offset);
2146 } else {
2147 return getRow(this->getRowGroupIndices()[rowGroup] + offset);
2148 }
2149}
2150
2151template<typename ValueType>
2153 STORM_LOG_ASSERT(rowGroup < this->getRowGroupCount(), "Row group is out-of-bounds.");
2154 STORM_LOG_ASSERT(offset < this->getRowGroupSize(rowGroup), "Row offset in row-group is out-of-bounds.");
2155 if (!this->hasTrivialRowGrouping()) {
2156 return getRow(this->getRowGroupIndices()[rowGroup] + offset);
2157 } else {
2158 STORM_LOG_ASSERT(offset == 0, "Invalid offset.");
2159 return getRow(rowGroup + offset);
2160 }
2161}
2162
2163template<typename ValueType>
2165 STORM_LOG_ASSERT(rowGroup < this->getRowGroupCount(), "Row group is out-of-bounds.");
2166 if (!this->hasTrivialRowGrouping()) {
2167 return getRows(this->getRowGroupIndices()[rowGroup], this->getRowGroupIndices()[rowGroup + 1]);
2168 } else {
2169 return getRows(rowGroup, rowGroup + 1);
2170 }
2171}
2172
2173template<typename ValueType>
2175 STORM_LOG_ASSERT(rowGroup < this->getRowGroupCount(), "Row group is out-of-bounds.");
2176 if (!this->hasTrivialRowGrouping()) {
2177 return getRows(this->getRowGroupIndices()[rowGroup], this->getRowGroupIndices()[rowGroup + 1]);
2178 } else {
2179 return getRows(rowGroup, rowGroup + 1);
2180 }
2181}
2182
2183template<typename ValueType>
2185 STORM_LOG_ASSERT(row < this->getRowCount(), "Row " << row << " exceeds row count " << this->getRowCount() << ".");
2186 return this->columnsAndValues.begin() + this->rowIndications[row];
2187}
2188
2189template<typename ValueType>
2191 STORM_LOG_ASSERT(row < this->getRowCount(), "Row " << row << " exceeds row count " << this->getRowCount() << ".");
2192 return this->columnsAndValues.begin() + this->rowIndications[row];
2193}
2194
2195template<typename ValueType>
2197 return this->columnsAndValues.begin();
2198}
2199
2200template<typename ValueType>
2202 return this->columnsAndValues.begin();
2203}
2204
2205template<typename ValueType>
2207 STORM_LOG_ASSERT(row < this->getRowCount(), "Row " << row << " exceeds row count " << this->getRowCount() << ".");
2208 return this->columnsAndValues.begin() + this->rowIndications[row + 1];
2209}
2210
2211template<typename ValueType>
2213 STORM_LOG_ASSERT(row < this->getRowCount(), "Row " << row << " exceeds row count " << this->getRowCount() << ".");
2214 return this->columnsAndValues.begin() + this->rowIndications[row + 1];
2215}
2216
2217template<typename ValueType>
2219 return this->columnsAndValues.begin() + this->rowIndications[rowCount];
2220}
2221
2222template<typename ValueType>
2224 return this->columnsAndValues.begin() + this->rowIndications[rowCount];
2225}
2226
2227template<typename ValueType>
2229 ValueType sum = storm::utility::zero<ValueType>();
2230 for (const_iterator it = this->begin(row), ite = this->end(row); it != ite; ++it) {
2231 sum += it->getValue();
2232 }
2233 return sum;
2234}
2235
2236template<typename ValueType>
2238 index_type nonConstEntries = 0;
2239 for (auto const& entry : *this) {
2240 if (!storm::utility::isConstant(entry.getValue())) {
2241 ++nonConstEntries;
2242 }
2243 }
2244 return nonConstEntries;
2245}
2246
2247template<typename ValueType>
2249 index_type nonConstRowGroups = 0;
2250 for (index_type rowGroup = 0; rowGroup < this->getRowGroupCount(); ++rowGroup) {
2251 for (auto const& entry : this->getRowGroup(rowGroup)) {
2252 if (!storm::utility::isConstant(entry.getValue())) {
2253 ++nonConstRowGroups;
2254 break;
2255 }
2256 }
2257 }
2258 return nonConstRowGroups;
2259}
2260
2261template<typename ValueType>
2264 for (index_type row = 0; row < this->rowCount; ++row) {
2265 auto rowSum = getRowSum(row);
2266 if (!comparator.isOne(rowSum)) {
2267 return false;
2268 }
2269 }
2270 for (auto const& entry : *this) {
2271 if (comparator.isConstant(entry.getValue())) {
2272 if (comparator.isLess(entry.getValue(), storm::utility::zero<ValueType>())) {
2273 return false;
2274 }
2275 }
2276 }
2277 return true;
2278}
2279
2280template<typename ValueType>
2283 for (auto const& entry : *this) {
2284 if (!comparator.isLess(storm::utility::zero<ValueType>(), entry.getValue())) {
2285 return false;
2286 }
2287 }
2288 return true;
2289}
2290
2291template<typename ValueType>
2292template<typename OtherValueType>
2294 // Check for matching sizes.
2295 if (this->getRowCount() != matrix.getRowCount())
2296 return false;
2297 if (this->getColumnCount() != matrix.getColumnCount())
2298 return false;
2299 if (this->hasTrivialRowGrouping() && !matrix.hasTrivialRowGrouping())
2300 return false;
2301 if (!this->hasTrivialRowGrouping() && matrix.hasTrivialRowGrouping())
2302 return false;
2303 if (!this->hasTrivialRowGrouping() && !matrix.hasTrivialRowGrouping() && this->getRowGroupIndices() != matrix.getRowGroupIndices())
2304 return false;
2305 if (this->getRowGroupIndices() != matrix.getRowGroupIndices())
2306 return false;
2307
2308 // Check the subset property for all rows individually.
2309 for (index_type row = 0; row < this->getRowCount(); ++row) {
2310 auto it2 = matrix.begin(row);
2311 auto ite2 = matrix.end(row);
2312 for (const_iterator it1 = this->begin(row), ite1 = this->end(row); it1 != ite1; ++it1) {
2313 // Skip over all entries of the other matrix that are before the current entry in the current matrix.
2314 while (it2 != ite2 && it2->getColumn() < it1->getColumn()) {
2315 ++it2;
2316 }
2317 if (it2 == ite2 || it1->getColumn() != it2->getColumn()) {
2318 return false;
2319 }
2320 }
2321 }
2322 return true;
2323}
2324
2325template<typename ValueType>
2327 if (this->getRowCount() != this->getColumnCount()) {
2328 return false;
2329 }
2330 if (this->getNonzeroEntryCount() != this->getRowCount()) {
2331 return false;
2332 }
2333 for (uint64_t row = 0; row < this->getRowCount(); ++row) {
2334 bool rowHasEntry = false;
2335 for (auto const& entry : this->getRow(row)) {
2336 if (entry.getColumn() == row) {
2337 if (!storm::utility::isOne(entry.getValue())) {
2338 return false;
2339 }
2340 rowHasEntry = true;
2341 } else {
2342 if (!storm::utility::isZero(entry.getValue())) {
2343 return false;
2344 }
2345 }
2346 }
2347 if (!rowHasEntry) {
2348 return false;
2349 }
2350 }
2351 return true;
2352}
2353
2354template<typename ValueType>
2356 std::string result =
2357 std::to_string(getRowCount()) + "x" + std::to_string(getColumnCount()) + " matrix (" + std::to_string(getNonzeroEntryCount()) + " non-zeroes";
2358 if (!hasTrivialRowGrouping()) {
2359 result += ", " + std::to_string(getRowGroupCount()) + " groups";
2360 }
2361 result += ")";
2362 return result;
2363}
2364
2365template<typename ValueType>
2366std::ostream& operator<<(std::ostream& out, SparseMatrix<ValueType> const& matrix) {
2367 // Print column numbers in header.
2368 out << "\t\t";
2369 for (typename SparseMatrix<ValueType>::index_type i = 0; i < matrix.getColumnCount(); ++i) {
2370 out << i << "\t";
2371 }
2372 out << '\n';
2373
2374 // Iterate over all row groups.
2375 for (typename SparseMatrix<ValueType>::index_type group = 0; group < matrix.getRowGroupCount(); ++group) {
2376 out << "\t---- group " << group << "/" << (matrix.getRowGroupCount() - 1) << " ---- \n";
2377 typename SparseMatrix<ValueType>::index_type start = matrix.hasTrivialRowGrouping() ? group : matrix.getRowGroupIndices()[group];
2378 typename SparseMatrix<ValueType>::index_type end = matrix.hasTrivialRowGrouping() ? group + 1 : matrix.getRowGroupIndices()[group + 1];
2379
2380 for (typename SparseMatrix<ValueType>::index_type i = start; i < end; ++i) {
2381 typename SparseMatrix<ValueType>::index_type nextIndex = matrix.rowIndications[i];
2382
2383 // Print the actual row.
2384 out << i << "\t(\t";
2385 typename SparseMatrix<ValueType>::index_type currentRealIndex = 0;
2386 while (currentRealIndex < matrix.columnCount) {
2387 if (nextIndex < matrix.rowIndications[i + 1] && currentRealIndex == matrix.columnsAndValues[nextIndex].getColumn()) {
2388 out << matrix.columnsAndValues[nextIndex].getValue() << "\t";
2389 ++nextIndex;
2390 } else {
2391 out << "0\t";
2392 }
2393 ++currentRealIndex;
2394 }
2395 out << "\t)\t" << i << '\n';
2396 }
2397 }
2398
2399 // Print column numbers in footer.
2400 out << "\t\t";
2401 for (typename SparseMatrix<ValueType>::index_type i = 0; i < matrix.getColumnCount(); ++i) {
2402 out << i << "\t";
2403 }
2404 out << '\n';
2405
2406 return out;
2407}
2408
2409template<typename ValueType>
2411 // Iterate over all row groups.
2412 for (typename SparseMatrix<ValueType>::index_type group = 0; group < this->getRowGroupCount(); ++group) {
2413 STORM_LOG_ASSERT(this->getRowGroupSize(group) == 1, "Incorrect row group size.");
2414 for (typename SparseMatrix<ValueType>::index_type i = this->getRowGroupIndices()[group]; i < this->getRowGroupIndices()[group + 1]; ++i) {
2415 typename SparseMatrix<ValueType>::index_type nextIndex = this->rowIndications[i];
2416
2417 // Print the actual row.
2418 out << i << "\t(";
2419 typename SparseMatrix<ValueType>::index_type currentRealIndex = 0;
2420 while (currentRealIndex < this->columnCount) {
2421 if (nextIndex < this->rowIndications[i + 1] && currentRealIndex == this->columnsAndValues[nextIndex].getColumn()) {
2422 out << this->columnsAndValues[nextIndex].getValue() << " ";
2423 ++nextIndex;
2424 } else {
2425 out << "0 ";
2426 }
2427 ++currentRealIndex;
2428 }
2429 out << ";\n";
2430 }
2431 }
2432}
2433
2434template<typename ValueType>
2436 std::size_t result = 0;
2437
2438 boost::hash_combine(result, this->getRowCount());
2439 boost::hash_combine(result, this->getColumnCount());
2440 boost::hash_combine(result, this->getEntryCount());
2441 boost::hash_combine(result, boost::hash_range(columnsAndValues.begin(), columnsAndValues.end()));
2442 boost::hash_combine(result, boost::hash_range(rowIndications.begin(), rowIndications.end()));
2443 if (!this->hasTrivialRowGrouping()) {
2444 boost::hash_combine(result, boost::hash_range(rowGroupIndices.get().begin(), rowGroupIndices.get().end()));
2445 }
2446
2447 return result;
2448}
2449
2450// Explicitly instantiate the entry, builder and the matrix.
2451// double
2453template std::ostream& operator<<(std::ostream& out, MatrixEntry<typename SparseMatrix<double>::index_type, double> const& entry);
2454template class SparseMatrixBuilder<double>;
2455template class SparseMatrix<double>;
2456template std::ostream& operator<<(std::ostream& out, SparseMatrix<double> const& matrix);
2458 typename SparseMatrix<double>::index_type const& row) const;
2459template std::vector<double> SparseMatrix<double>::getPointwiseProductRowSumVector(storm::storage::SparseMatrix<double> const& otherMatrix) const;
2460template bool SparseMatrix<double>::isSubmatrixOf(SparseMatrix<double> const& matrix) const;
2461
2462template class MatrixEntry<uint32_t, double>;
2463template std::ostream& operator<<(std::ostream& out, MatrixEntry<uint32_t, double> const& entry);
2464
2465// int
2467template std::ostream& operator<<(std::ostream& out, MatrixEntry<typename SparseMatrix<int>::index_type, int> const& entry);
2468template class SparseMatrixBuilder<int>;
2469template class SparseMatrix<int>;
2470template std::ostream& operator<<(std::ostream& out, SparseMatrix<int> const& matrix);
2471template bool SparseMatrix<int>::isSubmatrixOf(SparseMatrix<int> const& matrix) const;
2472
2473// state_type
2475template std::ostream& operator<<(
2479template std::ostream& operator<<(std::ostream& out, SparseMatrix<storm::storage::sparse::state_type> const& matrix);
2481
2482#ifdef STORM_HAVE_CARL
2483// Rational Numbers
2484
2485#if defined(STORM_HAVE_CLN)
2487template std::ostream& operator<<(std::ostream& out, MatrixEntry<typename SparseMatrix<ClnRationalNumber>::index_type, ClnRationalNumber> const& entry);
2489template class SparseMatrix<ClnRationalNumber>;
2490template std::ostream& operator<<(std::ostream& out, SparseMatrix<ClnRationalNumber> const& matrix);
2493template std::vector<storm::ClnRationalNumber> SparseMatrix<ClnRationalNumber>::getPointwiseProductRowSumVector(
2496#endif
2497
2498#if defined(STORM_HAVE_GMP)
2500template std::ostream& operator<<(std::ostream& out, MatrixEntry<typename SparseMatrix<GmpRationalNumber>::index_type, GmpRationalNumber> const& entry);
2502template class SparseMatrix<GmpRationalNumber>;
2503template std::ostream& operator<<(std::ostream& out, SparseMatrix<GmpRationalNumber> const& matrix);
2506template std::vector<storm::GmpRationalNumber> SparseMatrix<GmpRationalNumber>::getPointwiseProductRowSumVector(
2509#endif
2510
2511// Rational Function
2513template std::ostream& operator<<(std::ostream& out, MatrixEntry<typename SparseMatrix<RationalFunction>::index_type, RationalFunction> const& entry);
2515template class SparseMatrix<RationalFunction>;
2516template std::ostream& operator<<(std::ostream& out, SparseMatrix<RationalFunction> const& matrix);
2520 typename SparseMatrix<storm::RationalFunction>::index_type const& row) const;
2522 typename SparseMatrix<storm::RationalFunction>::index_type const& row) const;
2523template std::vector<storm::RationalFunction> SparseMatrix<RationalFunction>::getPointwiseProductRowSumVector(
2525template std::vector<storm::RationalFunction> SparseMatrix<double>::getPointwiseProductRowSumVector(
2527template std::vector<storm::RationalFunction> SparseMatrix<int>::getPointwiseProductRowSumVector(
2530
2531// Intervals
2532template std::vector<storm::Interval> SparseMatrix<double>::getPointwiseProductRowSumVector(
2533 storm::storage::SparseMatrix<storm::Interval> const& otherMatrix) const;
2535template std::ostream& operator<<(std::ostream& out, MatrixEntry<typename SparseMatrix<Interval>::index_type, Interval> const& entry);
2536template class SparseMatrixBuilder<Interval>;
2537template class SparseMatrix<Interval>;
2538template std::ostream& operator<<(std::ostream& out, SparseMatrix<Interval> const& matrix);
2539template std::vector<storm::Interval> SparseMatrix<Interval>::getPointwiseProductRowSumVector(
2540 storm::storage::SparseMatrix<storm::Interval> const& otherMatrix) const;
2542
2544#endif
2545
2546} // namespace storage
2547} // namespace storm
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:18
void setMultiple(uint64_t bitIndex, uint64_t nrOfBits, bool newValue=true)
Sets multiple bits to the given value.
uint_fast64_t getNextSetIndex(uint_fast64_t startingIndex) const
Retrieves the index of the bit that is the next bit set to true in the bit vector.
bool empty() const
Retrieves whether no bits are set to true in this bit vector.
const_iterator begin() const
Returns an iterator to the indices of the set bits in the bit vector.
void set(uint_fast64_t index, bool value=true)
Sets the given truth value at the given index.
size_t size() const
Retrieves the number of bits this bit vector can store.
uint_fast64_t getNextUnsetIndex(uint_fast64_t startingIndex) const
Retrieves the index of the bit that is the next bit set to false in the bit vector.
uint_fast64_t getNumberOfSetBits() const
Returns the number of bits that are set to true in this bit vector.
std::vector< uint_fast64_t > getNumberOfSetBitsBeforeIndices() const
Retrieves a vector that holds at position i the number of bits set before index i.
bool get(uint_fast64_t index) const
Retrieves the truth value of the bit at the given index and performs a bound check.
MatrixEntry operator*(value_type factor) const
Multiplies the entry with the given factor and returns the result.
value_type const & getValue() const
Retrieves the value of the matrix entry.
std::pair< index_type, value_type > const & getColumnValuePair() const
Retrieves a pair of column and value that characterizes this entry.
void setColumn(index_type const &column)
Sets the column of the current entry.
MatrixEntry()=default
index_type const & getColumn() const
Retrieves the column of the matrix entry.
bool operator!=(MatrixEntry const &other) const
void setValue(value_type const &value)
Sets the value of the entry in the matrix.
ValueType value_type
bool operator==(MatrixEntry const &other) const
This class represents a number of consecutive rows of the matrix.
const_rows(const_iterator begin, index_type entryCount)
Constructs an object that represents the rows defined by the value of the first entry,...
const_iterator end() const
Retrieves an iterator that points past the last entry of the rows.
index_type getNumberOfEntries() const
Retrieves the number of entries in the rows.
const_iterator begin() const
Retrieves an iterator that points to the beginning of the rows.
This class represents a number of consecutive rows of the matrix.
iterator end()
Retrieves an iterator that points past the last entry of the rows.
iterator begin()
Retrieves an iterator that points to the beginning of the rows.
index_type getNumberOfEntries() const
Retrieves the number of entries in the rows.
rows(iterator begin, index_type entryCount)
Constructs an object that represents the rows defined by the value of the first entry,...
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 replaceColumns(std::vector< index_type > const &replacements, index_type offset)
Replaces all columns with id > offset according to replacements.
SparseMatrixBuilder(index_type rows=0, index_type columns=0, index_type entries=0, bool forceDimensions=true, bool hasCustomRowGrouping=false, index_type rowGroups=0)
Constructs a sparse matrix builder producing a matrix with the given number of rows,...
void newRowGroup(index_type startingRow)
Starts a new row group in the matrix.
index_type getLastColumn() const
Retrieves the most recently used row.
void addDiagonalEntry(index_type row, ValueType const &value)
Makes sure that a diagonal entry will be inserted at the given row.
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.
void divideRowsInPlace(std::vector< value_type > const &divisors)
Divides each row of the matrix, i.e., divides each element in row i with divisors[i].
void convertToEquationSystem()
Transforms the matrix into an equation system.
SparseMatrix()
Constructs an empty sparse matrix.
void swapRows(index_type const &row1, index_type const &row2)
Swaps the two rows.
bool operator==(SparseMatrix< value_type > const &other) const
Determines whether the current and the given matrix are semantically equal.
const_rows getRow(index_type row) const
Returns an object representing the given row.
index_type getSizeOfLargestRowGroup() const
Returns the size of the largest row group of the matrix.
SparseMatrix selectRowsFromRowGroups(std::vector< index_type > const &rowGroupToRowIndexMapping, bool insertDiagonalEntries=true) const
Selects exactly one row from each row group of this matrix and returns the resulting matrix.
void multiplyWithVectorForward(std::vector< value_type > const &vector, std::vector< value_type > &result, std::vector< value_type > const *summand=nullptr) const
index_type getEntryCount() const
Returns the number of entries in the matrix.
const_rows getRows(index_type startRow, index_type endRow) const
Returns an object representing the consecutive rows given by the parameters.
index_type getNonconstantEntryCount() const
Returns the number of non-constant entries.
void multiplyVectorWithMatrix(std::vector< value_type > const &vector, std::vector< value_type > &result) const
Multiplies the vector to the matrix from the left and writes the result to the given result vector.
void multiplyAndReduceBackward(storm::solver::OptimizationDirection const &dir, std::vector< uint64_t > const &rowGroupIndices, std::vector< ValueType > const &vector, std::vector< ValueType > const *b, std::vector< ValueType > &result, std::vector< uint64_t > *choices) const
index_type getNumRowsInRowGroups(storm::storage::BitVector const &groupConstraint) const
Returns the total number of rows that are in one of the specified row groups.
void makeRowsAbsorbing(storm::storage::BitVector const &rows, bool dropZeroEntries=false)
This function makes the given rows absorbing.
std::vector< ResultValueType > getPointwiseProductRowSumVector(storm::storage::SparseMatrix< OtherValueType > const &otherMatrix) const
Performs a pointwise matrix multiplication of the matrix with the given matrix and returns a vector c...
void multiplyWithVector(std::vector< value_type > const &vector, std::vector< value_type > &result, std::vector< value_type > const *summand=nullptr) const
Multiplies the matrix with the given vector and writes the result to the given result vector.
void updateNonzeroEntryCount() const
Recompute the nonzero entry count.
bool isProbabilistic() const
Checks for each row whether it sums to one.
void multiplyWithVectorBackward(std::vector< value_type > const &vector, std::vector< value_type > &result, std::vector< value_type > const *summand=nullptr) const
SparseMatrix getSubmatrix(bool useGroups, storm::storage::BitVector const &rowConstraint, storm::storage::BitVector const &columnConstraint, bool insertDiagonalEntries=false, storm::storage::BitVector const &makeZeroColumns=storm::storage::BitVector()) const
Creates a submatrix of the current matrix by dropping all rows and columns whose bits are not set to ...
void performWalkerChaeStep(std::vector< ValueType > const &x, std::vector< ValueType > const &columnSums, std::vector< ValueType > const &b, std::vector< ValueType > const &ax, std::vector< ValueType > &result) const
Performs one step of the Walker-Chae technique.
friend class SparseMatrixBuilder< ValueType >
void updateDimensions() const
Recomputes the number of columns and the number of non-zero entries.
const_iterator begin(index_type row) const
Retrieves an iterator that points to the beginning of the given row.
void printAsMatlabMatrix(std::ostream &out) const
Prints the matrix in a dense format, as also used by e.g.
void performSuccessiveOverRelaxationStep(ValueType omega, std::vector< ValueType > &x, std::vector< ValueType > const &b) const
Performs one step of the successive over-relaxation technique.
std::vector< value_type > getConstrainedRowSumVector(storm::storage::BitVector const &rowConstraint, storm::storage::BitVector const &columnConstraint) const
Computes a vector whose i-th entry is the sum of the entries in the i-th selected row where only thos...
BitVector duplicateRowsInRowgroups() const
Finds duplicate rows in a rowgroup.
bool compareRows(index_type i1, index_type i2) const
Compares two rows.
const_rows getRowGroup(index_type rowGroup) const
Returns an object representing the given row group.
SparseMatrix permuteRows(std::vector< index_type > const &inversePermutation) const
Permute rows of the matrix according to the vector.
void setRowGroupIndices(std::vector< index_type > const &newRowGroupIndices)
Sets the row grouping to the given one.
void negateAllNonDiagonalEntries()
Negates (w.r.t.
const_iterator begin() const
Retrieves an iterator that points to the beginning of the first row of the matrix.
std::vector< index_type > swapRowGroupIndices(std::vector< index_type > &&newRowGrouping)
Swaps the grouping of rows of this matrix.
SparseMatrix restrictRows(storm::storage::BitVector const &rowsToKeep, bool allowEmptyRowGroups=false) const
Restrict rows in grouped rows matrix.
std::vector< ValueType > getRowSumVector() const
Sums the entries in all rows.
value_type multiplyRowWithVector(index_type row, std::vector< value_type > const &vector) const
Multiplies a single row of the matrix with the given vector and returns the result.
SparseMatrix< ValueType > transposeSelectedRowsFromRowGroups(std::vector< uint64_t > const &rowGroupChoices, bool keepZeros=false) const
Transposes the matrix w.r.t.
void multiplyAndReduceForward(storm::solver::OptimizationDirection const &dir, std::vector< uint64_t > const &rowGroupIndices, std::vector< ValueType > const &vector, std::vector< ValueType > const *b, std::vector< ValueType > &result, std::vector< uint64_t > *choices) const
value_type getRowSum(index_type row) const
Computes the sum of the entries in a given row.
const_iterator end(index_type row) const
Retrieves an iterator that points past the end of the given row.
SparseMatrix permuteRowGroupsAndColumns(std::vector< index_type > const &inverseRowGroupPermutation, std::vector< index_type > const &columnPermutation) const
Permutes row groups and columns of the matrix according to the given permutations.
void multiplyAndReduce(storm::solver::OptimizationDirection const &dir, std::vector< uint64_t > const &rowGroupIndices, std::vector< ValueType > const &vector, std::vector< ValueType > const *summand, std::vector< ValueType > &result, std::vector< uint64_t > *choices) const
Multiplies the matrix with the given vector, reduces it according to the given direction and and writ...
index_type getRowGroupCount() const
Returns the number of row groups in the matrix.
void dropZeroEntries()
Removes all zero entries from this.
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...
ResultValueType getPointwiseProductRowSum(storm::storage::SparseMatrix< OtherValueType > const &otherMatrix, index_type const &row) const
Performs a pointwise multiplication of the entries in the given row of this matrix and the entries of...
SparseMatrix< value_type > & operator=(SparseMatrix< value_type > const &other)
Assigns the contents of the given matrix to the current one by deep-copying its contents.
void makeRowGroupsAbsorbing(storm::storage::BitVector const &rowGroupConstraint, bool dropZeroEntries=false)
This function makes the groups of rows given by the bit vector absorbing.
std::string getDimensionsAsString() const
Returns a string describing the dimensions of the matrix.
index_type getColumnCount() const
Returns the number of columns of the matrix.
std::pair< storm::storage::SparseMatrix< value_type >, std::vector< value_type > > getJacobiDecomposition() const
Calculates the Jacobi decomposition of this sparse matrix.
void makeRowDirac(index_type row, index_type column, bool dropZeroEntries=false)
This function makes the given row Dirac.
std::vector< MatrixEntry< index_type, value_type > >::const_iterator const_iterator
value_type getConstrainedRowSum(index_type row, storm::storage::BitVector const &columns) const
Sums the entries in the given row and columns.
void deleteDiagonalEntries(bool dropZeroEntries=false)
Sets all diagonal elements to zero.
bool hasTrivialRowGrouping() const
Retrieves whether the matrix has a trivial row grouping.
void invertDiagonal()
Inverts all entries on the diagonal, i.e.
storm::storage::BitVector getRowGroupFilter(storm::storage::BitVector const &rowConstraint, bool setIfForAllRowsInGroup) const
Returns the indices of all row groups selected by the row constraints.
void makeRowGroupingTrivial()
Makes the row grouping of this matrix trivial.
SparseMatrix selectRowsFromRowIndexSequence(std::vector< index_type > const &rowIndexSequence, bool insertDiagonalEntries=true) const
Selects the rows that are given by the sequence of row indices, allowing to select rows arbitrarily o...
std::size_t hash() const
Calculates a hash value over all values contained in the matrix.
std::vector< index_type > const & getRowGroupIndices() const
Returns the grouping of rows of this matrix.
std::vector< MatrixEntry< index_type, value_type > >::iterator iterator
index_type getRowGroupSize(index_type group) const
Returns the size of the given row group.
std::vector< value_type > getConstrainedRowGroupSumVector(storm::storage::BitVector const &rowGroupConstraint, storm::storage::BitVector const &columnConstraint) const
Computes a vector whose entries represent the sums of selected columns for all rows in selected row g...
storm::storage::SparseMatrix< value_type > transpose(bool joinGroups=false, bool keepZeros=false) const
Transposes the matrix.
bool hasOnlyPositiveEntries() const
Checks whether each present entry is strictly positive (omitted entries are not considered).
friend std::ostream & operator<<(std::ostream &out, SparseMatrix< TPrime > const &matrix)
index_type getRowCount() const
Returns the number of rows of the matrix.
SparseMatrixIndexType index_type
index_type getNonzeroEntryCount() const
Returns the cached number of nonzero entries in the matrix.
const_iterator end() const
Retrieves an iterator that points past the end of the last row of the matrix.
index_type getNonconstantRowGroupCount() const
Returns the number of rowGroups that contain a non-constant value.
void scaleRowsInPlace(std::vector< value_type > const &factors)
Scales each row of the matrix, i.e., multiplies each element in row i with factors[i].
index_type getRowGroupEntryCount(index_type const group) const
Returns the number of entries in the given row group of the matrix.
storm::storage::BitVector getRowFilter(storm::storage::BitVector const &groupConstraint) const
Returns a bitvector representing the set of rows, with all indices set that correspond to one of the ...
SparseMatrix filterEntries(storm::storage::BitVector const &rowFilter) const
Returns a copy of this matrix that only considers entries in the selected rows.
bool isOne(ValueType const &value) const
bool isConstant(ValueType const &value) const
bool isLess(ValueType const &value1, ValueType const &value2) const
#define STORM_LOG_WARN(message)
Definition logging.h:30
#define STORM_LOG_TRACE(message)
Definition logging.h:17
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
Expression ite(Expression const &condition, Expression const &thenExpression, Expression const &elseExpression)
void print(std::vector< typename SparseMatrix< ValueType >::index_type > const &rowGroupIndices, std::vector< MatrixEntry< typename SparseMatrix< ValueType >::index_type, typename SparseMatrix< ValueType >::value_type > > const &columnsAndValues, std::vector< typename SparseMatrix< ValueType >::index_type > const &rowIndications)
std::ostream & operator<<(std::ostream &out, ParameterRegion< ParametricType > const &region)
bool isValidPermutation(std::vector< index_type > const &permutation)
Returns true if the given vector is a permutation of the numbers 0, 1, ..., n-1 for n = permutation....
std::vector< T > buildVectorForRange(T min, T max)
Constructs a vector [min, min+1, ...., max-1].
Definition vector.h:133
bool isOne(ValueType const &a)
Definition constants.cpp:36
bool isConstant(ValueType const &)
Definition constants.cpp:66
bool isZero(ValueType const &a)
Definition constants.cpp:41
LabParser.cpp.
Definition cli.cpp:18