Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
SubmatrixBuilder.h
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <boost/optional/optional.hpp>
5#include <cstdint>
6#include <limits>
7#include <vector>
8
15
16namespace storm::storage {
17
23template<typename ValueType>
25 public:
28
30 : matrix(matrix), originalToSubColumnIndex(matrix.getColumnCount(), std::numeric_limits<uint64_t>::max()) {
31 // Intentionally left empty.
32 }
33
48 template<typename RowConstraintType, typename ColumnConstraintType>
49 SparseMatrix<ValueType> getByRowConstraint(RowConstraintType const& rowConstraint, ColumnConstraintType const& columnConstraint,
50 bool const insertDiagonalElement,
52 return build<false>(rowConstraint, columnConstraint, insertDiagonalElement, makeZeroColumns);
53 }
54
64 template<typename RowConstraintType, typename ColumnConstraintType>
65 SparseMatrix<ValueType> getByRowGroupConstraint(RowConstraintType const& rowGroupConstraint, ColumnConstraintType const& columnConstraint,
66 bool const insertDiagonalElement,
68 return build<true>(rowGroupConstraint, columnConstraint, insertDiagonalElement, makeZeroColumns);
69 }
70
71 private:
72 static constexpr uint64_t NotInSubmatrix = std::numeric_limits<uint64_t>::max();
73
74 struct Sizes {
75 index_type rows{0};
76 index_type groups{0};
77 index_type entryBound{0}; // upper bound on the number of entries (exact counting would require an additional, cache-unfriendly pass over the entries)
78 };
79
80 // Only used in assertions
81 template<typename ConstraintType>
82 static bool isStrictlyAscending(ConstraintType const& constraint) {
83 bool first = true;
84 uint64_t previous = 0;
85 for (uint64_t index : constraint) {
86 if (!first && index <= previous) {
87 return false;
88 }
89 first = false;
90 previous = index;
91 }
92 return true;
93 }
94
95 // Resets the used entries of the lookup table when leaving the scope (also if an exception is thrown).
96 template<typename ConstraintType>
97 struct ColumnMappingGuard {
98 ColumnMappingGuard(std::vector<uint64_t>& mapping, ConstraintType const& columnConstraint) : mapping(mapping), columnConstraint(columnConstraint) {}
99 ~ColumnMappingGuard() {
100 for (uint64_t col : columnConstraint) {
101 mapping[col] = NotInSubmatrix;
102 }
103 }
104 std::vector<uint64_t>& mapping;
105 ConstraintType const& columnConstraint;
106 };
107
113 template<bool GroupMode, typename ConstraintType, typename RowFunc, typename NewGroupFunc>
114 void forEachSelectedRow(ConstraintType const& rowConstraint, RowFunc&& f, NewGroupFunc&& g) const {
115 index_type subIndex = 0; // index of the current row (row mode) or row group (group mode) in the submatrix
116 if (matrix.hasTrivialRowGrouping()) {
117 // row==group
118 for (uint64_t row : rowConstraint) {
119 f(row, subIndex++);
120 }
121 } else {
122 auto const& groups = matrix.getRowGroupIndices();
123 if constexpr (GroupMode) {
124 for (uint64_t group : rowConstraint) {
125 g(groups[group]);
126 for (uint64_t row = groups[group], end = groups[group + 1]; row < end; ++row) {
127 f(row, subIndex);
128 }
129 ++subIndex;
130 }
131
132 } else {
133 // groups[currentGroup] <= row < groups[currentGroup + 1] holds for the current row after the update below.
134 index_type currentGroup = std::numeric_limits<index_type>::max();
135 index_type groupEnd = 0;
136 for (uint64_t row : rowConstraint) {
137 if (row >= groupEnd) {
138 // Binary search for the group of the row. Groups are skipped quickly if few rows are selected.
139 auto const searchStart = currentGroup == std::numeric_limits<index_type>::max() ? groups.begin() : groups.begin() + currentGroup + 1;
140 auto const it = std::upper_bound(searchStart, groups.end(), row);
141 currentGroup = std::distance(groups.begin(), it) - 1;
142 groupEnd = groups[currentGroup + 1];
143 g(row);
144 }
145 f(row, subIndex++);
146 }
147 }
148 }
149 }
150
151 template<bool GroupMode, typename RowConstraintType, typename ColumnConstraintType>
152 SparseMatrix<ValueType> build(RowConstraintType const& rowConstraint, ColumnConstraintType const& columnConstraint, bool const insertDiagonalElement,
153 storm::OptionalRef<storm::storage::BitVector const> const& makeZeroColumns) const {
154 STORM_LOG_THROW(!(rowConstraint.begin() == rowConstraint.end()) && !(columnConstraint.begin() == columnConstraint.end()),
155 storm::exceptions::InvalidArgumentException, "Cannot build empty submatrix.");
156 STORM_LOG_ASSERT(isStrictlyAscending(rowConstraint), "Row (group) constraint must be iterated in strictly ascending order.");
157 STORM_LOG_ASSERT(isStrictlyAscending(columnConstraint), "Column constraint must be iterated in strictly ascending order.");
158 STORM_LOG_ASSERT(!makeZeroColumns || makeZeroColumns->size() == matrix.getColumnCount(), "Dimension mismatch.");
159 bool const hasGroups = !matrix.hasTrivialRowGrouping();
160
161 // Step 1: Fill the lookup table.
162 // Columns that are made zero keep a sub index (so that column indices are consistent with the submatrix that does not have makeZeroColumns)
163 // but are from that point on treated as non-existent.
164 index_type subColumnCount = 0;
165 for (uint64_t col : columnConstraint) {
166 if (!makeZeroColumns || !makeZeroColumns->get(col)) {
167 originalToSubColumnIndex[col] = subColumnCount;
168 }
169 ++subColumnCount;
170 }
171 ColumnMappingGuard<ColumnConstraintType> guard(originalToSubColumnIndex, columnConstraint);
172
173 // Step 2: Determine the number of rows and groups and an upper bound for the number of entries of the result.
174 Sizes sizes;
175 forEachSelectedRow<GroupMode>(
176 rowConstraint,
177 [&](uint64_t row, index_type /*diagonalIndex*/) {
178 ++sizes.rows;
179 sizes.entryBound += (matrix.end(row) - matrix.begin(row)) + (insertDiagonalElement ? 1 : 0);
180 },
181 [&](uint64_t) { ++sizes.groups; });
182
183 // Step 3: Fill the result.
184 std::vector<index_type> rowIndications;
185 rowIndications.reserve(sizes.rows + 1);
186 std::vector<entry_type> entries;
187 entries.reserve(sizes.entryBound);
188 boost::optional<std::vector<index_type>> rowGroupIndices;
189 if (hasGroups) {
190 rowGroupIndices.emplace();
191 rowGroupIndices->reserve(sizes.groups + 1);
192 }
193 forEachSelectedRow<GroupMode>(
194 rowConstraint,
195 [&](uint64_t row, index_type diagonalIndex) {
196 rowIndications.push_back(entries.size());
197 copyRow(row, diagonalIndex, subColumnCount, insertDiagonalElement, entries);
198 },
199 [&](uint64_t) {
200 if (hasGroups) {
201 rowGroupIndices->push_back(rowIndications.size());
202 }
203 });
204 rowIndications.push_back(entries.size());
205 if (hasGroups) {
206 rowGroupIndices->push_back(rowIndications.size() - 1);
207 }
208 // Release excess memory if the bound was loose. If it was tight (e.g., when keeping most of the matrix), avoid the copy.
209 if (entries.size() < sizes.entryBound - sizes.entryBound / 4) {
210 entries.shrink_to_fit();
211 }
212 return SparseMatrix<ValueType>(subColumnCount, std::move(rowIndications), std::move(entries), std::move(rowGroupIndices));
213 }
214
218 void copyRow(uint64_t row, index_type diagonalIndex, index_type subColumnCount, bool insertDiagonalElement, std::vector<entry_type>& entries) const {
219 bool diagonalMissing = insertDiagonalElement && diagonalIndex < subColumnCount;
220 for (auto it = matrix.begin(row), ite = matrix.end(row); it != ite; ++it) {
221 uint64_t const subColumn = originalToSubColumnIndex[it->getColumn()];
222 if (subColumn == NotInSubmatrix) {
223 continue;
224 }
225 if (diagonalMissing && subColumn >= diagonalIndex) {
226 if (subColumn > diagonalIndex) {
227 entries.emplace_back(diagonalIndex, storm::utility::zero<ValueType>());
228 }
229 diagonalMissing = false;
230 }
231 entries.emplace_back(subColumn, it->getValue());
232 }
233 if (diagonalMissing) {
234 entries.emplace_back(diagonalIndex, storm::utility::zero<ValueType>());
235 }
236 }
237
238 SparseMatrix<ValueType> const& matrix;
239 // Maps original column indices to submatrix column indices. Entries are NotInSubmatrix outside of calls to the get... methods.
240 mutable std::vector<uint64_t> originalToSubColumnIndex;
241};
242
243} // namespace storm::storage
Helper class that optionally holds a reference to an object of type T.
Definition OptionalRef.h:48
A class that holds a possibly non-square matrix in the compressed row storage format.
SparseMatrixIndexType index_type
SparseMatrix< ValueType > getByRowGroupConstraint(RowConstraintType const &rowGroupConstraint, ColumnConstraintType const &columnConstraint, bool const insertDiagonalElement, storm::OptionalRef< storm::storage::BitVector const > makeZeroColumns=storm::NullRef) const
Creates the submatrix that keeps only the given row groups and columns The row group structure of the...
SubmatrixBuilder(SparseMatrix< ValueType > const &matrix)
typename SparseMatrix< ValueType >::index_type index_type
SparseMatrix< ValueType > getByRowConstraint(RowConstraintType const &rowConstraint, ColumnConstraintType const &columnConstraint, bool const insertDiagonalElement, storm::OptionalRef< storm::storage::BitVector const > makeZeroColumns=storm::NullRef) const
Creates the submatrix that keeps only the given rows and columns If the matrix has a non-trivial row ...
MatrixEntry< index_type, ValueType > entry_type
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:39
ValueType zero()
Definition constants.cpp:26
OptionalRef(T &) -> OptionalRef< T >
deduction guides
constexpr NullRefType NullRef
Definition OptionalRef.h:31