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