Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
SubmatrixBuilderTest.cpp
Go to the documentation of this file.
1#include "storm-config.h"
2#include "test/storm_gtest.h"
3
4#include <random>
5#include <vector>
6
11
12namespace {
13
14storm::storage::SparseMatrix<double> createRandomMatrix(std::mt19937_64& rng, uint64_t groups, uint64_t columns, bool grouped, double density) {
15 std::uniform_real_distribution<double> coin(0.0, 1.0);
16 std::uniform_int_distribution<uint64_t> groupSize(1, 3);
17 storm::storage::SparseMatrixBuilder<double> builder(0, columns, 0, true, grouped);
18 uint64_t row = 0;
19 for (uint64_t g = 0; g < groups; ++g) {
20 uint64_t const size = grouped ? groupSize(rng) : 1;
21 if (grouped) {
22 builder.newRowGroup(row);
23 }
24 for (uint64_t i = 0; i < size; ++i, ++row) {
25 for (uint64_t c = 0; c < columns; ++c) {
26 if (coin(rng) < density) {
27 builder.addNextValue(row, c, coin(rng) + 0.5);
28 }
29 }
30 }
31 }
32 return builder.build(row, columns, grouped ? groups : 0);
33}
34
35storm::storage::BitVector randomBitVector(std::mt19937_64& rng, uint64_t size, double p) {
36 std::uniform_real_distribution<double> coin(0.0, 1.0);
37 storm::storage::BitVector result(size);
38 for (uint64_t i = 0; i < size; ++i) {
39 result.set(i, coin(rng) < p);
40 }
41 if (result.empty()) {
42 result.set(rng() % size);
43 }
44 return result;
45}
46
47// Straightforward reference implementation (the former implementation of SparseMatrix::getSubmatrix).
48storm::storage::SparseMatrix<double> referenceSubmatrix(storm::storage::SparseMatrix<double> const& matrix, bool useGroups,
49 storm::storage::BitVector const& rowConstraint, storm::storage::BitVector const& columnConstraint,
50 bool insertDiagonalEntries, storm::storage::BitVector const& makeZeroColumns) {
51 using index_type = uint64_t;
52 std::vector<index_type> rowGroupIndices;
53 if (useGroups) {
54 rowGroupIndices = matrix.getRowGroupIndices();
55 } else {
56 for (index_type i = 0; i <= matrix.getRowCount(); ++i) {
57 rowGroupIndices.push_back(i);
58 }
59 }
60 index_type const submatrixColumnCount = columnConstraint.getNumberOfSetBits();
61 std::vector<index_type> columnBitsSetBeforeIndex = columnConstraint.getNumberOfSetBitsBeforeIndices();
62 std::vector<index_type> rowBitsSetBeforeIndex = rowConstraint.getNumberOfSetBitsBeforeIndices();
63 auto keep = [&](index_type col) { return columnConstraint.get(col) && (makeZeroColumns.size() == 0 || !makeZeroColumns.get(col)); };
64
65 index_type subEntries = 0, subRows = 0, rowGroupCount = 0;
66 for (uint64_t index : rowConstraint) {
67 subRows += rowGroupIndices[index + 1] - rowGroupIndices[index];
68 for (index_type i = rowGroupIndices[index]; i < rowGroupIndices[index + 1]; ++i) {
69 bool foundDiagonalElement = false;
70 for (auto it = matrix.begin(i); it != matrix.end(i); ++it) {
71 if (keep(it->getColumn())) {
72 ++subEntries;
73 if (columnBitsSetBeforeIndex[it->getColumn()] == rowBitsSetBeforeIndex[index]) {
74 foundDiagonalElement = true;
75 }
76 }
77 }
78 if (insertDiagonalEntries && !foundDiagonalElement && rowGroupCount < submatrixColumnCount) {
79 ++subEntries;
80 }
81 }
82 ++rowGroupCount;
83 }
84 bool const groupedResult = !matrix.hasTrivialRowGrouping();
85 storm::storage::SparseMatrixBuilder<double> builder(subRows, submatrixColumnCount, subEntries, true, groupedResult);
86 rowGroupCount = 0;
87 index_type rowCount = 0;
88 for (uint64_t index : rowConstraint) {
89 if (groupedResult) {
90 builder.newRowGroup(rowCount);
91 }
92 for (index_type i = rowGroupIndices[index]; i < rowGroupIndices[index + 1]; ++i) {
93 bool insertedDiagonalElement = false;
94 for (auto it = matrix.begin(i); it != matrix.end(i); ++it) {
95 if (keep(it->getColumn())) {
96 if (columnBitsSetBeforeIndex[it->getColumn()] == rowBitsSetBeforeIndex[index]) {
97 insertedDiagonalElement = true;
98 } else if (insertDiagonalEntries && !insertedDiagonalElement && columnBitsSetBeforeIndex[it->getColumn()] > rowBitsSetBeforeIndex[index]) {
99 builder.addNextValue(rowCount, rowGroupCount, 0.0);
100 insertedDiagonalElement = true;
101 }
102 builder.addNextValue(rowCount, columnBitsSetBeforeIndex[it->getColumn()], it->getValue());
103 }
104 }
105 if (insertDiagonalEntries && !insertedDiagonalElement && rowGroupCount < submatrixColumnCount) {
106 builder.addNextValue(rowCount, rowGroupCount, 0.0);
107 }
108 ++rowCount;
109 }
110 ++rowGroupCount;
111 }
112 auto result = builder.build();
113 if (groupedResult && !useGroups) {
114 // Row groups of the result: one group for each original group that has at least one selected row.
115 std::vector<index_type> newGroups{0};
116 auto selectedRowIt = rowConstraint.begin();
117 for (index_type group = 0; group < matrix.getRowGroupCount(); ++group) {
118 index_type newRowCount = 0;
119 while (selectedRowIt != rowConstraint.end() && *selectedRowIt < matrix.getRowGroupIndices()[group + 1]) {
120 ++selectedRowIt;
121 ++newRowCount;
122 }
123 if (newRowCount > 0) {
124 newGroups.push_back(newGroups.back() + newRowCount);
125 }
126 }
127 result.setRowGroupIndices(newGroups);
128 }
129 return result;
130}
131
132void expectEqual(storm::storage::SparseMatrix<double> const& expected, storm::storage::SparseMatrix<double> const& actual) {
133 ASSERT_EQ(expected.getRowCount(), actual.getRowCount());
134 ASSERT_EQ(expected.getColumnCount(), actual.getColumnCount());
135 ASSERT_EQ(expected.getEntryCount(), actual.getEntryCount());
136 ASSERT_EQ(expected.hasTrivialRowGrouping(), actual.hasTrivialRowGrouping());
137 if (!expected.hasTrivialRowGrouping()) {
138 ASSERT_EQ(expected.getRowGroupIndices(), actual.getRowGroupIndices());
139 }
140 for (uint64_t row = 0; row < expected.getRowCount(); ++row) {
141 ASSERT_EQ(expected.getRow(row).getNumberOfEntries(), actual.getRow(row).getNumberOfEntries());
142 auto it = actual.begin(row);
143 for (auto const& entry : expected.getRow(row)) {
144 EXPECT_EQ(entry.getColumn(), it->getColumn());
145 EXPECT_EQ(entry.getValue(), it->getValue());
146 ++it;
147 }
148 }
149}
150
151} // namespace
152
153TEST(SubmatrixBuilderTest, AgreesWithGetSubmatrix) {
154 std::mt19937_64 rng(42);
155 for (bool grouped : {false, true}) {
156 for (int iteration = 0; iteration < 200; ++iteration) {
157 uint64_t const n = 1 + rng() % 12;
158 auto matrix = createRandomMatrix(rng, n, n, grouped, 0.4);
160 // Reuse the builder to also check that the internal state is reset properly.
161 for (int j = 0; j < 5; ++j) {
162 auto const groupConstraint = randomBitVector(rng, matrix.getRowGroupCount(), 0.6);
163 auto const rowConstraint = randomBitVector(rng, matrix.getRowCount(), 0.6);
164 auto const columnConstraint = randomBitVector(rng, matrix.getColumnCount(), 0.6);
165 auto const zeroColumns = randomBitVector(rng, matrix.getColumnCount(), 0.3);
166 for (bool diagonal : {false, true}) {
167 expectEqual(referenceSubmatrix(matrix, true, groupConstraint, columnConstraint, diagonal, {}),
168 builder.getByRowGroupConstraint(groupConstraint, columnConstraint, diagonal));
169 expectEqual(referenceSubmatrix(matrix, true, groupConstraint, columnConstraint, diagonal, zeroColumns),
170 builder.getByRowGroupConstraint(groupConstraint, columnConstraint, diagonal, zeroColumns));
171 expectEqual(referenceSubmatrix(matrix, false, rowConstraint, columnConstraint, diagonal, {}),
172 builder.getByRowConstraint(rowConstraint, columnConstraint, diagonal));
173 expectEqual(referenceSubmatrix(matrix, false, rowConstraint, columnConstraint, diagonal, zeroColumns),
174 builder.getByRowConstraint(rowConstraint, columnConstraint, diagonal, zeroColumns));
175 }
176 }
177 }
178 }
179}
180
181TEST(SubmatrixBuilderTest, VectorConstraints) {
182 std::mt19937_64 rng(7);
183 auto matrix = createRandomMatrix(rng, 10, 10, true, 0.5);
185 storm::storage::BitVector groups(10, std::vector<uint64_t>{1, 2, 5, 9});
186 storm::storage::BitVector columns(10, std::vector<uint64_t>{0, 2, 3, 9});
187 std::vector<uint64_t> groupVector(groups.begin(), groups.end());
188 std::vector<uint64_t> columnVector(columns.begin(), columns.end());
189 expectEqual(builder.getByRowGroupConstraint(groups, columns, true), builder.getByRowGroupConstraint(groupVector, columnVector, true));
190}
191
192TEST(SubmatrixBuilderTest, EmptyConstraintThrows) {
193 std::mt19937_64 rng(1);
194 auto matrix = createRandomMatrix(rng, 4, 4, false, 0.5);
197 storm::storage::BitVector some(4, true);
198 STORM_SILENT_EXPECT_THROW(builder.getByRowConstraint(empty, some, false), storm::exceptions::InvalidArgumentException);
199 STORM_SILENT_EXPECT_THROW(builder.getByRowConstraint(some, empty, false), storm::exceptions::InvalidArgumentException);
200 // The builder is still usable afterwards
201 expectEqual(referenceSubmatrix(matrix, false, some, some, false, {}), builder.getByRowConstraint(some, some, false));
202}
TEST(SubmatrixBuilderTest, AgreesWithGetSubmatrix)
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
std::vector< uint64_t > getNumberOfSetBitsBeforeIndices() const
Retrieves a vector that holds at position i the number of bits set before index i.
uint64_t getNumberOfSetBits() const
Returns the number of bits that are set to true in this 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.
index_type getNumberOfEntries() const
Retrieves the number of entries in the rows.
A class that can be used to build a sparse matrix by adding value by value.
A class that holds a possibly non-square matrix in the compressed row storage format.
const_rows getRow(index_type row) const
Returns an object representing the given row.
index_type getEntryCount() const
Returns the number of entries in the matrix.
const_iterator begin(index_type row) const
Retrieves an iterator that points to the beginning of the given row.
const_iterator end(index_type row) const
Retrieves an iterator that points past the end of the given row.
index_type getRowGroupCount() const
Returns the number of row groups in the matrix.
index_type getColumnCount() const
Returns the number of columns of the matrix.
bool hasTrivialRowGrouping() const
Retrieves whether the matrix has a trivial row grouping.
std::vector< index_type > const & getRowGroupIndices() const
Returns the grouping of rows of this matrix.
index_type getRowCount() const
Returns the number of rows of the matrix.
Helper class to extract submatrices of a fixed sparse matrix.
SparseMatrix< ValueType > getByRowGroupConstraint(RowConstraintType const &rowGroupConstraint, ColumnConstraintType const &columnConstraint, bool const insertDiagonalElement, storm::OptionalRef< storm::storage::BitVector const > makeZeroColumns=storm::NullRef) const
Creates the submatrix that keeps only the given row groups and columns The row group structure of the...
SparseMatrix< ValueType > getByRowConstraint(RowConstraintType const &rowConstraint, ColumnConstraintType const &columnConstraint, bool const insertDiagonalElement, storm::OptionalRef< storm::storage::BitVector const > makeZeroColumns=storm::NullRef) const
Creates the submatrix that keeps only the given rows and columns If the matrix has a non-trivial row ...
#define STORM_SILENT_EXPECT_THROW(statement, expected_exception)
Definition storm_gtest.h:21