You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

864 lines
43 KiB

  1. #include <boost/functional/hash.hpp>
  2. // To detect whether the usage of TBB is possible, this include is neccessary
  3. #include "storm-config.h"
  4. #ifdef STORM_HAVE_INTELTBB
  5. #include "tbb/tbb.h"
  6. #endif
  7. #include "src/storage/SparseMatrix.h"
  8. #include "src/exceptions/InvalidStateException.h"
  9. #include "log4cplus/logger.h"
  10. #include "log4cplus/loggingmacros.h"
  11. extern log4cplus::Logger logger;
  12. namespace storm {
  13. namespace storage {
  14. template<typename T>
  15. SparseMatrixBuilder<T>::SparseMatrixBuilder(uint_fast64_t rows, uint_fast64_t columns, uint_fast64_t entries) : rowCountSet(rows != 0), rowCount(rows), columnCountSet(columns != 0), columnCount(columns), entryCount(entries), storagePreallocated(rows != 0 && columns != 0 && entries != 0), columnsAndValues(), rowIndications(), currentEntryCount(0), lastRow(0), lastColumn(0) {
  16. this->prepareInternalStorage();
  17. }
  18. template<typename T>
  19. void SparseMatrixBuilder<T>::addNextValue(uint_fast64_t row, uint_fast64_t column, T const& value) {
  20. // Depending on whether the internal data storage was preallocated or not, adding the value is done somewhat
  21. // differently.
  22. if (storagePreallocated) {
  23. // Check whether the given row and column positions are valid and throw error otherwise.
  24. if (row >= rowCount || column >= columnCount) {
  25. throw storm::exceptions::OutOfRangeException() << "Illegal call to SparseMatrixBuilder::addNextValue: adding entry at out-of-bounds position (" << row << ", " << column << ") in matrix of size (" << rowCount << ", " << columnCount << ").";
  26. }
  27. } else {
  28. if (rowCountSet) {
  29. if (row >= rowCount) {
  30. throw storm::exceptions::OutOfRangeException() << "Illegal call to SparseMatrixBuilder::addNextValue: adding entry at out-of-bounds row " << row << " in matrix with " << rowCount << " rows.";
  31. }
  32. }
  33. if (columnCountSet) {
  34. if (column >= columnCount) {
  35. throw storm::exceptions::OutOfRangeException() << "Illegal call to SparseMatrixBuilder::addNextValue: adding entry at out-of-bounds column " << column << " in matrix with " << columnCount << " columns.";
  36. }
  37. }
  38. }
  39. // Check that we did not move backwards wrt. the row.
  40. if (row < lastRow) {
  41. throw storm::exceptions::InvalidArgumentException() << "Illegal call to SparseMatrixBuilder::addNextValue: adding an element in row " << row << ", but an element in row " << lastRow << " has already been added.";
  42. }
  43. // Check that we did not move backwards wrt. to column.
  44. if (row == lastRow && column < lastColumn) {
  45. throw storm::exceptions::InvalidArgumentException() << "Illegal call to SparseMatrixBuilder::addNextValue: adding an element in column " << column << " in row " << row << ", but an element in column " << lastColumn << " has already been added in that row.";
  46. }
  47. // If we switched to another row, we have to adjust the missing entries in the row indices vector.
  48. if (row != lastRow) {
  49. if (storagePreallocated) {
  50. // If the storage was preallocated, we can access the elements in the vectors with the subscript
  51. // operator.
  52. for (uint_fast64_t i = lastRow + 1; i <= row; ++i) {
  53. rowIndications[i] = currentEntryCount;
  54. }
  55. } else {
  56. // Otherwise, we need to push the correct values to the vectors, which might trigger reallocations.
  57. for (uint_fast64_t i = lastRow + 1; i <= row; ++i) {
  58. rowIndications.push_back(currentEntryCount);
  59. }
  60. }
  61. lastRow = row;
  62. }
  63. lastColumn = column;
  64. // Finally, set the element and increase the current size.
  65. if (storagePreallocated) {
  66. columnsAndValues[currentEntryCount] = std::make_pair(column, value);
  67. } else {
  68. columnsAndValues.emplace_back(column, value);
  69. if (!columnCountSet) {
  70. columnCount = std::max(columnCount, column + 1);
  71. }
  72. if (!rowCountSet) {
  73. rowCount = row + 1;
  74. }
  75. }
  76. ++currentEntryCount;
  77. }
  78. template<typename T>
  79. SparseMatrix<T> SparseMatrixBuilder<T>::build(uint_fast64_t overriddenRowCount, uint_fast64_t overriddenColumnCount) {
  80. // Check whether it's safe to finalize the matrix and throw error otherwise.
  81. if (storagePreallocated && currentEntryCount != entryCount) {
  82. throw storm::exceptions::InvalidStateException() << "Illegal call to SparseMatrix::finalize: expected " << entryCount << " entries, but got " << currentEntryCount << " instead.";
  83. } else {
  84. // Fill in the missing entries in the row indices array, as there may be empty rows at the end.
  85. if (storagePreallocated) {
  86. for (uint_fast64_t i = lastRow + 1; i < rowCount; ++i) {
  87. rowIndications[i] = currentEntryCount;
  88. }
  89. } else {
  90. if (!rowCountSet) {
  91. rowCount = std::max(overriddenRowCount, rowCount);
  92. }
  93. for (uint_fast64_t i = lastRow + 1; i < rowCount; ++i) {
  94. rowIndications.push_back(currentEntryCount);
  95. }
  96. }
  97. // We put a sentinel element at the last position of the row indices array. This eases iteration work,
  98. // as now the indices of row i are always between rowIndications[i] and rowIndications[i + 1], also for
  99. // the first and last row.
  100. if (storagePreallocated) {
  101. rowIndications[rowCount] = currentEntryCount;
  102. } else {
  103. rowIndications.push_back(currentEntryCount);
  104. if (!columnCountSet) {
  105. columnCount = std::max(columnCount, overriddenColumnCount);
  106. }
  107. }
  108. entryCount = currentEntryCount;
  109. }
  110. return SparseMatrix<T>(columnCount, std::move(rowIndications), std::move(columnsAndValues));
  111. }
  112. template<typename T>
  113. void SparseMatrixBuilder<T>::prepareInternalStorage() {
  114. // Only allocate the memory if the dimensions of the matrix are already known.
  115. if (storagePreallocated) {
  116. columnsAndValues = std::vector<std::pair<uint_fast64_t, T>>(entryCount, std::make_pair(0, storm::utility::constantZero<T>()));
  117. rowIndications = std::vector<uint_fast64_t>(rowCount + 1, 0);
  118. } else {
  119. rowIndications.push_back(0);
  120. }
  121. }
  122. template<typename T>
  123. SparseMatrix<T>::rows::rows(iterator begin, uint_fast64_t entryCount) : beginIterator(begin), entryCount(entryCount) {
  124. // Intentionally left empty.
  125. }
  126. template<typename T>
  127. typename SparseMatrix<T>::iterator SparseMatrix<T>::rows::begin() {
  128. return beginIterator;
  129. }
  130. template<typename T>
  131. typename SparseMatrix<T>::iterator SparseMatrix<T>::rows::end() {
  132. return beginIterator + entryCount;
  133. }
  134. template<typename T>
  135. SparseMatrix<T>::const_rows::const_rows(const_iterator begin, uint_fast64_t entryCount) : beginIterator(begin), entryCount(entryCount) {
  136. // Intentionally left empty.
  137. }
  138. template<typename T>
  139. typename SparseMatrix<T>::const_iterator SparseMatrix<T>::const_rows::begin() const {
  140. return beginIterator;
  141. }
  142. template<typename T>
  143. typename SparseMatrix<T>::const_iterator SparseMatrix<T>::const_rows::end() const {
  144. return beginIterator + entryCount;
  145. }
  146. template<typename T>
  147. SparseMatrix<T>::SparseMatrix() : rowCount(0), columnCount(0), entryCount(0), columnsAndValues(), rowIndications() {
  148. // Intentionally left empty.
  149. }
  150. template<typename T>
  151. SparseMatrix<T>::SparseMatrix(SparseMatrix<T> const& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), columnsAndValues(other.columnsAndValues), rowIndications(other.rowIndications) {
  152. // Intentionally left empty.
  153. }
  154. template<typename T>
  155. SparseMatrix<T>::SparseMatrix(SparseMatrix<T>&& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), columnsAndValues(std::move(other.columnsAndValues)), rowIndications(std::move(other.rowIndications)) {
  156. // Now update the source matrix
  157. other.rowCount = 0;
  158. other.columnCount = 0;
  159. other.entryCount = 0;
  160. }
  161. template<typename T>
  162. SparseMatrix<T>::SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t> const& rowIndications, std::vector<std::pair<uint_fast64_t, T>> const& columnsAndValues) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), columnsAndValues(columnsAndValues), rowIndications(rowIndications) {
  163. // Intentionally left empty.
  164. }
  165. template<typename T>
  166. SparseMatrix<T>::SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t>&& rowIndications, std::vector<std::pair<uint_fast64_t, T>>&& columnsAndValues) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), columnsAndValues(std::move(columnsAndValues)), rowIndications(std::move(rowIndications)) {
  167. // Intentionally left empty.
  168. }
  169. template<typename T>
  170. SparseMatrix<T>& SparseMatrix<T>::operator=(SparseMatrix<T> const& other) {
  171. // Only perform assignment if source and target are not the same.
  172. if (this != &other) {
  173. rowCount = other.rowCount;
  174. columnCount = other.columnCount;
  175. entryCount = other.entryCount;
  176. columnsAndValues = other.columnsAndValues;
  177. rowIndications = other.rowIndications;
  178. }
  179. return *this;
  180. }
  181. template<typename T>
  182. SparseMatrix<T>& SparseMatrix<T>::operator=(SparseMatrix<T>&& other) {
  183. // Only perform assignment if source and target are not the same.
  184. if (this != &other) {
  185. rowCount = other.rowCount;
  186. columnCount = other.columnCount;
  187. entryCount = other.entryCount;
  188. columnsAndValues = std::move(other.columnsAndValues);
  189. rowIndications = std::move(other.rowIndications);
  190. }
  191. return *this;
  192. }
  193. template<typename T>
  194. bool SparseMatrix<T>::operator==(SparseMatrix<T> const& other) const {
  195. if (this == &other) {
  196. return true;
  197. }
  198. bool equalityResult = true;
  199. equalityResult &= rowCount == other.rowCount;
  200. equalityResult &= columnCount == other.columnCount;
  201. // For the actual contents, we need to do a little bit more work, because we want to ignore elements that
  202. // are set to zero, please they may be represented implicitly in the other matrix.
  203. for (uint_fast64_t row = 0; row < this->getRowCount(); ++row) {
  204. for (const_iterator it1 = this->begin(row), ite1 = this->end(row), it2 = other.begin(row), ite2 = other.end(row); it1 != ite1 && it2 != ite2; ++it1, ++it2) {
  205. // Skip over all zero entries in both matrices.
  206. while (it1 != ite1 && it1->second == storm::utility::constantZero<T>()) {
  207. ++it1;
  208. }
  209. while (it2 != ite2 && it2->second == storm::utility::constantZero<T>()) {
  210. ++it2;
  211. }
  212. if ((it1 == ite1) || (it2 == ite2)) {
  213. equalityResult = (it1 == ite1) ^ (it2 == ite2);
  214. break;
  215. } else {
  216. if (it1->first != it2->first || it1->second != it2->second) {
  217. equalityResult = false;
  218. break;
  219. }
  220. }
  221. }
  222. }
  223. return equalityResult;
  224. }
  225. template<typename T>
  226. uint_fast64_t SparseMatrix<T>::getRowCount() const {
  227. return rowCount;
  228. }
  229. template<typename T>
  230. uint_fast64_t SparseMatrix<T>::getColumnCount() const {
  231. return columnCount;
  232. }
  233. template<typename T>
  234. uint_fast64_t SparseMatrix<T>::getEntryCount() const {
  235. return entryCount;
  236. }
  237. template<typename T>
  238. void SparseMatrix<T>::makeRowsAbsorbing(storm::storage::BitVector const& rows) {
  239. for (auto row : rows) {
  240. makeRowAbsorbing(row, row);
  241. }
  242. }
  243. template<typename T>
  244. void SparseMatrix<T>::makeRowsAbsorbing(storm::storage::BitVector const& rowGroupConstraint, std::vector<uint_fast64_t> const& rowGroupIndices) {
  245. for (auto rowGroup : rowGroupConstraint) {
  246. for (uint_fast64_t row = rowGroupIndices[rowGroup]; row < rowGroupIndices[rowGroup + 1]; ++row) {
  247. makeRowAbsorbing(row, rowGroup);
  248. }
  249. }
  250. }
  251. template<typename T>
  252. void SparseMatrix<T>::makeRowAbsorbing(uint_fast64_t row, uint_fast64_t column) {
  253. if (row > rowCount) {
  254. throw storm::exceptions::OutOfRangeException() << "Illegal call to SparseMatrix::makeRowAbsorbing: access to row " << row << " is out of bounds.";
  255. }
  256. iterator columnValuePtr = this->begin(row);
  257. iterator columnValuePtrEnd = this->end(row);
  258. // If the row has no elements in it, we cannot make it absorbing, because we would need to move all elements
  259. // in the vector of nonzeros otherwise.
  260. if (columnValuePtr >= columnValuePtrEnd) {
  261. throw storm::exceptions::InvalidStateException() << "Illegal call to SparseMatrix::makeRowAbsorbing: cannot make row " << row << " absorbing, but there is no entry in this row.";
  262. }
  263. // If there is at least one entry in this row, we can just set it to one, modify its column value to the
  264. // one given by the parameter and set all subsequent elements of this row to zero.
  265. columnValuePtr->first = column;
  266. columnValuePtr->second = storm::utility::constantOne<T>();
  267. ++columnValuePtr;
  268. for (; columnValuePtr != columnValuePtrEnd; ++columnValuePtr) {
  269. columnValuePtr->first = 0;
  270. columnValuePtr->second = storm::utility::constantZero<T>();
  271. }
  272. }
  273. template<typename T>
  274. T SparseMatrix<T>::getConstrainedRowSum(uint_fast64_t row, storm::storage::BitVector const& constraint) const {
  275. T result(0);
  276. for (const_iterator it = this->begin(row), ite = this->end(row); it != ite; ++it) {
  277. if (constraint.get(it->first)) {
  278. result += it->second;
  279. }
  280. }
  281. return result;
  282. }
  283. template<typename T>
  284. std::vector<T> SparseMatrix<T>::getConstrainedRowSumVector(storm::storage::BitVector const& rowConstraint, storm::storage::BitVector const& columnConstraint) const {
  285. std::vector<T> result(rowConstraint.getNumberOfSetBits());
  286. uint_fast64_t currentRowCount = 0;
  287. for (auto row : rowConstraint) {
  288. result[currentRowCount++] = getConstrainedRowSum(row, columnConstraint);
  289. }
  290. return result;
  291. }
  292. template<typename T>
  293. std::vector<T> SparseMatrix<T>::getConstrainedRowSumVector(storm::storage::BitVector const& rowGroupConstraint, std::vector<uint_fast64_t> const& rowGroupIndices, storm::storage::BitVector const& columnConstraint) const {
  294. std::vector<T> result;
  295. result.reserve(rowGroupConstraint.getNumberOfSetBits());
  296. for (auto rowGroup : rowGroupConstraint) {
  297. for (uint_fast64_t row = rowGroupIndices[rowGroup]; row < rowGroupIndices[rowGroup + 1]; ++row) {
  298. result.push_back(getConstrainedRowSum(row, columnConstraint));
  299. }
  300. }
  301. return result;
  302. }
  303. template<typename T>
  304. SparseMatrix<T> SparseMatrix<T>::getSubmatrix(storm::storage::BitVector const& constraint) const {
  305. // Create a fake row grouping to reduce this to a call to a more general method.
  306. std::vector<uint_fast64_t> rowGroupIndices(rowCount + 1);
  307. uint_fast64_t i = 0;
  308. for (std::vector<uint_fast64_t>::iterator it = rowGroupIndices.begin(); it != rowGroupIndices.end(); ++it, ++i) {
  309. *it = i;
  310. }
  311. return getSubmatrix(constraint, constraint, rowGroupIndices);
  312. }
  313. template<typename T>
  314. SparseMatrix<T> SparseMatrix<T>::getSubmatrix(storm::storage::BitVector const& rowGroupConstraint, std::vector<uint_fast64_t> const& rowGroupIndices, bool insertDiagonalEntries) const {
  315. return getSubmatrix(rowGroupConstraint, rowGroupConstraint, rowGroupIndices, insertDiagonalEntries);
  316. }
  317. template<typename T>
  318. SparseMatrix<T> SparseMatrix<T>::getSubmatrix(storm::storage::BitVector const& rowGroupConstraint, storm::storage::BitVector const& columnConstraint, std::vector<uint_fast64_t> const& rowGroupIndices, bool insertDiagonalEntries) const {
  319. // First, we need to determine the number of entries and the number of rows of the submatrix.
  320. uint_fast64_t subEntries = 0;
  321. uint_fast64_t subRows = 0;
  322. for (auto index : rowGroupConstraint) {
  323. subRows += rowGroupIndices[index + 1] - rowGroupIndices[index];
  324. for (uint_fast64_t i = rowGroupIndices[index]; i < rowGroupIndices[index + 1]; ++i) {
  325. bool foundDiagonalElement = false;
  326. for (const_iterator it = this->begin(i), ite = this->end(i); it != ite; ++it) {
  327. if (columnConstraint.get(it->first)) {
  328. ++subEntries;
  329. if (index == it->first) {
  330. foundDiagonalElement = true;
  331. }
  332. }
  333. }
  334. // If requested, we need to reserve one entry more for inserting the diagonal zero entry.
  335. if (insertDiagonalEntries && !foundDiagonalElement) {
  336. ++subEntries;
  337. }
  338. }
  339. }
  340. // Create and initialize resulting matrix.
  341. SparseMatrixBuilder<T> matrixBuilder(subRows, columnConstraint.getNumberOfSetBits(), subEntries);
  342. // Create a temporary vector that stores for each index whose bit is set to true the number of bits that
  343. // were set before that particular index.
  344. std::vector<uint_fast64_t> bitsSetBeforeIndex;
  345. bitsSetBeforeIndex.reserve(columnCount);
  346. // Compute the information to fill this vector.
  347. uint_fast64_t lastIndex = 0;
  348. uint_fast64_t currentNumberOfSetBits = 0;
  349. // If we are requested to add missing diagonal entries, we need to make sure the corresponding rows are also
  350. // taken.
  351. storm::storage::BitVector columnBitCountConstraint = columnConstraint;
  352. if (insertDiagonalEntries) {
  353. columnBitCountConstraint |= rowGroupConstraint;
  354. }
  355. for (auto index : columnBitCountConstraint) {
  356. while (lastIndex <= index) {
  357. bitsSetBeforeIndex.push_back(currentNumberOfSetBits);
  358. ++lastIndex;
  359. }
  360. ++currentNumberOfSetBits;
  361. }
  362. // Copy over selected entries.
  363. uint_fast64_t rowCount = 0;
  364. for (auto index : rowGroupConstraint) {
  365. for (uint_fast64_t i = rowGroupIndices[index]; i < rowGroupIndices[index + 1]; ++i) {
  366. bool insertedDiagonalElement = false;
  367. for (const_iterator it = this->begin(i), ite = this->end(i); it != ite; ++it) {
  368. if (columnConstraint.get(it->first)) {
  369. if (index == it->first) {
  370. insertedDiagonalElement = true;
  371. } else if (insertDiagonalEntries && !insertedDiagonalElement && it->first > index) {
  372. matrixBuilder.addNextValue(rowCount, bitsSetBeforeIndex[index], storm::utility::constantZero<T>());
  373. insertedDiagonalElement = true;
  374. }
  375. matrixBuilder.addNextValue(rowCount, bitsSetBeforeIndex[it->first], it->second);
  376. }
  377. }
  378. if (insertDiagonalEntries && !insertedDiagonalElement) {
  379. matrixBuilder.addNextValue(rowCount, bitsSetBeforeIndex[index], storm::utility::constantZero<T>());
  380. }
  381. ++rowCount;
  382. }
  383. }
  384. return matrixBuilder.build();
  385. }
  386. template<typename T>
  387. SparseMatrix<T> SparseMatrix<T>::getSubmatrix(std::vector<uint_fast64_t> const& rowGroupToRowIndexMapping, std::vector<uint_fast64_t> const& rowGroupIndices, bool insertDiagonalEntries) const {
  388. // First, we need to count how many non-zero entries the resulting matrix will have and reserve space for
  389. // diagonal entries if requested.
  390. uint_fast64_t subEntries = 0;
  391. for (uint_fast64_t rowGroupIndex = 0, rowGroupIndexEnd = rowGroupToRowIndexMapping.size(); rowGroupIndex < rowGroupIndexEnd; ++rowGroupIndex) {
  392. // Determine which row we need to select from the current row group.
  393. uint_fast64_t rowToCopy = rowGroupIndices[rowGroupIndex] + rowGroupToRowIndexMapping[rowGroupIndex];
  394. // Iterate through that row and count the number of slots we have to reserve for copying.
  395. bool foundDiagonalElement = false;
  396. for (const_iterator it = this->begin(rowToCopy), ite = this->end(rowToCopy); it != ite; ++it) {
  397. if (it->first == rowGroupIndex) {
  398. foundDiagonalElement = true;
  399. }
  400. ++subEntries;
  401. }
  402. if (insertDiagonalEntries && !foundDiagonalElement) {
  403. ++subEntries;
  404. }
  405. }
  406. // Now create the matrix to be returned with the appropriate size.
  407. SparseMatrixBuilder<T> matrixBuilder(rowGroupIndices.size() - 1, columnCount, subEntries);
  408. // Copy over the selected lines from the source matrix.
  409. for (uint_fast64_t rowGroupIndex = 0, rowGroupIndexEnd = rowGroupToRowIndexMapping.size(); rowGroupIndex < rowGroupIndexEnd; ++rowGroupIndex) {
  410. // Determine which row we need to select from the current row group.
  411. uint_fast64_t rowToCopy = rowGroupIndices[rowGroupIndex] + rowGroupToRowIndexMapping[rowGroupIndex];
  412. // Iterate through that row and copy the entries. This also inserts a zero element on the diagonal if
  413. // there is no entry yet.
  414. bool insertedDiagonalElement = false;
  415. for (const_iterator it = this->begin(rowToCopy), ite = this->end(rowToCopy); it != ite; ++it) {
  416. if (it->first == rowGroupIndex) {
  417. insertedDiagonalElement = true;
  418. } else if (insertDiagonalEntries && !insertedDiagonalElement && it->first > rowGroupIndex) {
  419. matrixBuilder.addNextValue(rowGroupIndex, rowGroupIndex, storm::utility::constantZero<T>());
  420. insertedDiagonalElement = true;
  421. }
  422. matrixBuilder.addNextValue(rowGroupIndex, it->first, it->second);
  423. }
  424. if (insertDiagonalEntries && !insertedDiagonalElement) {
  425. matrixBuilder.addNextValue(rowGroupIndex, rowGroupIndex, storm::utility::constantZero<T>());
  426. }
  427. }
  428. // Finalize created matrix and return result.
  429. return matrixBuilder.build();
  430. }
  431. template <typename T>
  432. SparseMatrix<T> SparseMatrix<T>::transpose() const {
  433. uint_fast64_t rowCount = this->columnCount;
  434. uint_fast64_t columnCount = this->rowCount;
  435. uint_fast64_t entryCount = this->entryCount;
  436. std::vector<uint_fast64_t> rowIndications(rowCount + 1);
  437. std::vector<std::pair<uint_fast64_t, T>> columnsAndValues(entryCount);
  438. // First, we need to count how many entries each column has.
  439. for (uint_fast64_t row = 0; row < this->rowCount; ++row) {
  440. for (const_iterator it = this->begin(row), ite = this->end(row); it != ite; ++it) {
  441. if (it->second > 0) {
  442. ++rowIndications[it->first + 1];
  443. }
  444. }
  445. }
  446. // Now compute the accumulated offsets.
  447. for (uint_fast64_t i = 1; i < rowCount + 1; ++i) {
  448. rowIndications[i] = rowIndications[i - 1] + rowIndications[i];
  449. }
  450. // Create an array that stores the index for the next value to be added for
  451. // each row in the transposed matrix. Initially this corresponds to the previously
  452. // computed accumulated offsets.
  453. std::vector<uint_fast64_t> nextIndices = rowIndications;
  454. // Now we are ready to actually fill in the values of the transposed matrix.
  455. for (uint_fast64_t row = 0; row < this->rowCount; ++row) {
  456. for (const_iterator it = this->begin(row), ite = this->end(row); it != ite; ++it) {
  457. if (it->second > 0) {
  458. columnsAndValues[nextIndices[it->first]] = std::make_pair(row, it->second);
  459. nextIndices[it->first]++;
  460. }
  461. }
  462. }
  463. storm::storage::SparseMatrix<T> transposedMatrix(columnCount, std::move(rowIndications), std::move(columnsAndValues));
  464. return transposedMatrix;
  465. }
  466. template<typename T>
  467. void SparseMatrix<T>::convertToEquationSystem() {
  468. invertDiagonal();
  469. negateAllNonDiagonalEntries();
  470. }
  471. template<typename T>
  472. void SparseMatrix<T>::invertDiagonal() {
  473. // Check if the matrix is square, because only then it makes sense to perform this
  474. // transformation.
  475. if (this->getRowCount() != this->getColumnCount()) {
  476. throw storm::exceptions::InvalidArgumentException() << "SparseMatrix::invertDiagonal requires the Matrix to be square!";
  477. }
  478. // Now iterate over all rows and set the diagonal elements to the inverted value.
  479. // If there is a row without the diagonal element, an exception is thrown.
  480. T one = storm::utility::constantOne<T>();
  481. bool foundDiagonalElement = false;
  482. for (uint_fast64_t row = 0; row < rowCount; ++row) {
  483. for (iterator it = this->begin(row), ite = this->end(row); it != ite; ++it) {
  484. if (it->first == row) {
  485. it->second = one - it->second;
  486. foundDiagonalElement = true;
  487. }
  488. }
  489. // Throw an exception if a row did not have an element on the diagonal.
  490. if (!foundDiagonalElement) {
  491. throw storm::exceptions::InvalidArgumentException() << "Illegal call to SparseMatrix::invertDiagonal: matrix is missing diagonal entries.";
  492. }
  493. }
  494. }
  495. template<typename T>
  496. void SparseMatrix<T>::negateAllNonDiagonalEntries() {
  497. // Check if the matrix is square, because only then it makes sense to perform this transformation.
  498. if (this->getRowCount() != this->getColumnCount()) {
  499. throw storm::exceptions::InvalidArgumentException() << "Illegal call to SparseMatrix::invertDiagonal: matrix is non-square.";
  500. }
  501. // Iterate over all rows and negate all the elements that are not on the diagonal.
  502. for (uint_fast64_t row = 0; row < rowCount; ++row) {
  503. for (iterator it = this->begin(row), ite = this->end(row); it != ite; ++it) {
  504. if (it->first != row) {
  505. it->second = -it->second;
  506. }
  507. }
  508. }
  509. }
  510. template<typename T>
  511. void SparseMatrix<T>::deleteDiagonalEntries() {
  512. // Check if the matrix is square, because only then it makes sense to perform this transformation.
  513. if (this->getRowCount() != this->getColumnCount()) {
  514. throw storm::exceptions::InvalidArgumentException() << "Illegal call to SparseMatrix::deleteDiagonalEntries: matrix is non-square.";
  515. }
  516. // Iterate over all rows and negate all the elements that are not on the diagonal.
  517. for (uint_fast64_t row = 0; row < rowCount; ++row) {
  518. for (iterator it = this->begin(row), ite = this->end(row); it != ite; ++it) {
  519. if (it->first == row) {
  520. it->second = storm::utility::constantZero<T>();
  521. }
  522. }
  523. }
  524. }
  525. template<typename T>
  526. typename std::pair<storm::storage::SparseMatrix<T>, storm::storage::SparseMatrix<T>> SparseMatrix<T>::getJacobiDecomposition() const {
  527. if (rowCount != columnCount) {
  528. throw storm::exceptions::InvalidArgumentException() << "Illegal call to SparseMatrix::invertDiagonal: matrix is non-square.";
  529. }
  530. storm::storage::SparseMatrix<T> resultLU(*this);
  531. resultLU.deleteDiagonalEntries();
  532. SparseMatrixBuilder<T> dInvBuilder(rowCount, columnCount, rowCount);
  533. // Copy entries to the appropriate matrices.
  534. for (uint_fast64_t rowNumber = 0; rowNumber < rowCount; ++rowNumber) {
  535. // Because the matrix may have several entries on the diagonal, we need to sum them before we are able
  536. // to invert the entry.
  537. T diagonalValue = storm::utility::constantZero<T>();
  538. for (const_iterator it = this->begin(rowNumber), ite = this->end(rowNumber); it != ite; ++it) {
  539. if (it->first == rowNumber) {
  540. diagonalValue += it->second;
  541. } else if (it->first > rowNumber) {
  542. break;
  543. }
  544. }
  545. dInvBuilder.addNextValue(rowNumber, rowNumber, storm::utility::constantOne<T>() / diagonalValue);
  546. }
  547. return std::make_pair(std::move(resultLU), dInvBuilder.build());
  548. }
  549. template<typename T>
  550. std::vector<T> SparseMatrix<T>::getPointwiseProductRowSumVector(storm::storage::SparseMatrix<T> const& otherMatrix) const {
  551. std::vector<T> result(rowCount, storm::utility::constantZero<T>());
  552. // Iterate over all elements of the current matrix and either continue with the next element in case the
  553. // given matrix does not have a non-zero element at this column position, or multiply the two entries and
  554. // add the result to the corresponding position in the vector.
  555. for (uint_fast64_t row = 0; row < rowCount && row < otherMatrix.rowCount; ++row) {
  556. for (const_iterator it1 = this->begin(row), ite1 = this->end(row), it2 = otherMatrix.begin(row), ite2 = otherMatrix.end(row); it1 != ite1 && it2 != ite2; ++it1) {
  557. if (it1->first < it2->first) {
  558. continue;
  559. } else {
  560. // If the precondition of this method (i.e. that the given matrix is a submatrix
  561. // of the current one) was fulfilled, we know now that the two elements are in
  562. // the same column, so we can multiply and add them to the row sum vector.
  563. result[row] += it2->second * it1->second;
  564. ++it2;
  565. }
  566. }
  567. }
  568. return result;
  569. }
  570. template<typename T>
  571. void SparseMatrix<T>::multiplyWithVector(std::vector<T> const& vector, std::vector<T>& result) const {
  572. #ifdef STORM_HAVE_INTELTBB
  573. tbb::parallel_for(tbb::blocked_range<uint_fast64_t>(0, result.size(), 10),
  574. [&] (tbb::blocked_range<uint_fast64_t> const& range) {
  575. uint_fast64_t startRow = range.begin();
  576. uint_fast64_t endRow = range.end();
  577. const_iterator it = this->begin(startRow);
  578. const_iterator ite;
  579. typename std::vector<uint_fast64_t>::const_iterator rowIterator = this->rowIndications.begin() + startRow;
  580. typename std::vector<uint_fast64_t>::const_iterator rowIteratorEnd = this->rowIndications.begin() + endRow;
  581. typename std::vector<T>::iterator resultIterator = result.begin() + startRow;
  582. typename std::vector<T>::iterator resultIteratorEnd = result.begin() + endRow;
  583. for (; resultIterator != resultIteratorEnd; ++rowIterator, ++resultIterator) {
  584. *resultIterator = storm::utility::constantZero<T>();
  585. for (ite = this->begin() + *(rowIterator + 1); it != ite; ++it) {
  586. *resultIterator += it->second * vector[it->first];
  587. }
  588. }
  589. });
  590. #else
  591. const_iterator it = this->begin();
  592. const_iterator ite;
  593. typename std::vector<uint_fast64_t>::const_iterator rowIterator = rowIndications.begin();
  594. typename std::vector<uint_fast64_t>::const_iterator rowIteratorEnd = rowIndications.end();
  595. typename std::vector<T>::iterator resultIterator = result.begin();
  596. typename std::vector<T>::iterator resultIteratorEnd = result.end();
  597. for (; resultIterator != resultIteratorEnd; ++rowIterator, ++resultIterator) {
  598. *resultIterator = storm::utility::constantZero<T>();
  599. for (ite = this->begin() + *(rowIterator + 1); it != ite; ++it) {
  600. *resultIterator += it->second * vector[it->first];
  601. }
  602. }
  603. #endif
  604. }
  605. template<typename T>
  606. uint_fast64_t SparseMatrix<T>::getSizeInMemory() const {
  607. uint_fast64_t size = sizeof(*this);
  608. // Add size of columns and values.
  609. size += sizeof(std::pair<uint_fast64_t, T>) * columnsAndValues.capacity();
  610. // Add row_indications size.
  611. size += sizeof(uint_fast64_t) * rowIndications.capacity();
  612. return size;
  613. }
  614. template<typename T>
  615. typename SparseMatrix<T>::const_rows SparseMatrix<T>::getRows(uint_fast64_t startRow, uint_fast64_t endRow) const {
  616. return const_rows(this->columnsAndValues.begin() + this->rowIndications[startRow], this->rowIndications[endRow + 1] - this->rowIndications[startRow]);
  617. }
  618. template<typename T>
  619. typename SparseMatrix<T>::rows SparseMatrix<T>::getRows(uint_fast64_t startRow, uint_fast64_t endRow) {
  620. return rows(this->columnsAndValues.begin() + this->rowIndications[startRow], this->rowIndications[endRow + 1] - this->rowIndications[startRow]);
  621. }
  622. template<typename T>
  623. typename SparseMatrix<T>::const_rows SparseMatrix<T>::getRow(uint_fast64_t row) const {
  624. return getRows(row, row);
  625. }
  626. template<typename T>
  627. typename SparseMatrix<T>::rows SparseMatrix<T>::getRow(uint_fast64_t row) {
  628. return getRows(row, row);
  629. }
  630. template<typename T>
  631. typename SparseMatrix<T>::const_iterator SparseMatrix<T>::begin(uint_fast64_t row) const {
  632. return this->columnsAndValues.begin() + this->rowIndications[row];
  633. }
  634. template<typename T>
  635. typename SparseMatrix<T>::iterator SparseMatrix<T>::begin(uint_fast64_t row) {
  636. return this->columnsAndValues.begin() + this->rowIndications[row];
  637. }
  638. template<typename T>
  639. typename SparseMatrix<T>::const_iterator SparseMatrix<T>::end(uint_fast64_t row) const {
  640. return this->columnsAndValues.begin() + this->rowIndications[row + 1];
  641. }
  642. template<typename T>
  643. typename SparseMatrix<T>::iterator SparseMatrix<T>::end(uint_fast64_t row) {
  644. return this->columnsAndValues.begin() + this->rowIndications[row + 1];
  645. }
  646. template<typename T>
  647. typename SparseMatrix<T>::const_iterator SparseMatrix<T>::end() const {
  648. return this->columnsAndValues.begin() + this->rowIndications[rowCount];
  649. }
  650. template<typename T>
  651. typename SparseMatrix<T>::iterator SparseMatrix<T>::end() {
  652. return this->columnsAndValues.begin() + this->rowIndications[rowCount];
  653. }
  654. template<typename T>
  655. T SparseMatrix<T>::getRowSum(uint_fast64_t row) const {
  656. T sum = storm::utility::constantZero<T>();
  657. for (const_iterator it = this->begin(row), ite = this->end(row); it != ite; ++it) {
  658. sum += it->second;
  659. }
  660. return sum;
  661. }
  662. template<typename T>
  663. bool SparseMatrix<T>::isSubmatrixOf(SparseMatrix<T> const& matrix) const {
  664. // Check for matching sizes.
  665. if (this->getRowCount() != matrix.getRowCount()) return false;
  666. if (this->getColumnCount() != matrix.getColumnCount()) return false;
  667. // Check the subset property for all rows individually.
  668. for (uint_fast64_t row = 0; row < this->getRowCount(); ++row) {
  669. for (const_iterator it1 = this->begin(row), ite1 = this->end(row), it2 = matrix.begin(row), ite2 = matrix.end(row); it1 != ite1; ++it1) {
  670. // Skip over all entries of the other matrix that are before the current entry in the current matrix.
  671. while (it2 != ite2 && it2->first < it1->first) {
  672. ++it2;
  673. }
  674. if (it2 == ite2 || it1->first != it2->first) {
  675. return false;
  676. }
  677. }
  678. }
  679. return true;
  680. }
  681. template<typename T>
  682. std::ostream& operator<<(std::ostream& out, SparseMatrix<T> const& matrix) {
  683. // Print column numbers in header.
  684. out << "\t\t";
  685. for (uint_fast64_t i = 0; i < matrix.columnCount; ++i) {
  686. out << i << "\t";
  687. }
  688. out << std::endl;
  689. // Iterate over all rows.
  690. for (uint_fast64_t i = 0; i < matrix.rowCount; ++i) {
  691. uint_fast64_t nextIndex = matrix.rowIndications[i];
  692. // Print the actual row.
  693. out << i << "\t(\t";
  694. uint_fast64_t currentRealIndex = 0;
  695. while (currentRealIndex < matrix.columnCount) {
  696. if (nextIndex < matrix.rowIndications[i + 1] && currentRealIndex == matrix.columnsAndValues[nextIndex].first) {
  697. out << matrix.columnsAndValues[nextIndex].second << "\t";
  698. ++nextIndex;
  699. } else {
  700. out << "0\t";
  701. }
  702. ++currentRealIndex;
  703. }
  704. out << "\t)\t" << i << std::endl;
  705. }
  706. // Print column numbers in footer.
  707. out << "\t\t";
  708. for (uint_fast64_t i = 0; i < matrix.columnCount; ++i) {
  709. out << i << "\t";
  710. }
  711. out << std::endl;
  712. return out;
  713. }
  714. template<typename T>
  715. std::size_t SparseMatrix<T>::hash() const {
  716. std::size_t result = 0;
  717. boost::hash_combine(result, rowCount);
  718. boost::hash_combine(result, columnCount);
  719. boost::hash_combine(result, entryCount);
  720. boost::hash_combine(result, boost::hash_range(columnsAndValues.begin(), columnsAndValues.end()));
  721. boost::hash_combine(result, boost::hash_range(rowIndications.begin(), rowIndications.end()));
  722. return result;
  723. }
  724. // Explicitly instantiate the builder and the matrix.
  725. template class SparseMatrixBuilder<double>;
  726. template class SparseMatrix<double>;
  727. template std::ostream& operator<<(std::ostream& out, SparseMatrix<double> const& matrix);
  728. template class SparseMatrixBuilder<int>;
  729. template class SparseMatrix<int>;
  730. template std::ostream& operator<<(std::ostream& out, SparseMatrix<int> const& matrix);
  731. } // namespace storage
  732. } // namespace storm