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