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