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