Morpheus 1.0.0
Dynamic matrix type and algorithms for sparse matrices
Loading...
Searching...
No Matches
Morpheus_MatrixMarket_Impl.hpp
1
24#ifndef MORPHEUS_MATRIX_MARKET_IMPL_HPP
25#define MORPHEUS_MATRIX_MARKET_IMPL_HPP
26
27#include <vector>
28#include <string>
29#include <fstream>
30#include <sstream>
31#include <iostream>
32#include <algorithm>
33
34#include <Morpheus_Exceptions.hpp>
35#include <Morpheus_CooMatrix.hpp>
36#include <Morpheus_DenseMatrix.hpp>
37#include <Morpheus_Sort.hpp>
38#include <Morpheus_Copy.hpp>
39
40namespace Morpheus {
41namespace IO {
42namespace Impl {
43
44inline void tokenize(std::vector<std::string>& tokens, const std::string& str,
45 const std::string& delimiters = "\n\r\t ") {
46 // Skip delimiters at beginning.
47 std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
48 // Find first "non-delimiter".
49 std::string::size_type pos = str.find_first_of(delimiters, lastPos);
50
51 while (std::string::npos != pos || std::string::npos != lastPos) {
52 // Found a token, add it to the vector.
53 tokens.push_back(str.substr(lastPos, pos - lastPos));
54 // Skip delimiters. Note the "not_of"
55 lastPos = str.find_first_not_of(delimiters, pos);
56 // Find next "non-delimiter"
57 pos = str.find_first_of(delimiters, lastPos);
58 }
59}
60
62 std::string storage; // "array" or "coordinate"
63 std::string symmetry; // "general", "symmetric"
64 // "hermitian", or "skew-symmetric"
65 std::string type; // "complex", "real", "integer", or "pattern"
66};
67
68template <typename Stream>
69void read_matrix_market_banner(matrix_market_banner& banner, Stream& input) {
70 std::string line;
71 std::vector<std::string> tokens;
72
73 // read first line
74 std::getline(input, line);
75 Impl::tokenize(tokens, line);
76
77 if (tokens.size() != 5 || tokens[0] != "%%MatrixMarket" ||
78 tokens[1] != "matrix")
79 throw Morpheus::IOException("invalid MatrixMarket banner");
80
81 banner.storage = tokens[2];
82 banner.type = tokens[3];
83 banner.symmetry = tokens[4];
84
85 if (banner.storage != "array" && banner.storage != "coordinate")
86 throw Morpheus::IOException("invalid MatrixMarket storage format [" +
87 banner.storage + "]");
88
89 if (banner.type != "complex" && banner.type != "real" &&
90 banner.type != "integer" && banner.type != "pattern")
91 throw Morpheus::IOException("invalid MatrixMarket data type [" +
92 banner.type + "]");
93
94 if (banner.symmetry != "general" && banner.symmetry != "symmetric" &&
95 banner.symmetry != "unsymmetric" && banner.symmetry != "hermitian" &&
96 banner.symmetry != "skew-symmetric")
97 throw Morpheus::IOException("invalid MatrixMarket symmetry [" +
98 banner.symmetry + "]");
99}
100
101template <typename T, typename... P, typename Stream>
102void read_coordinate_stream(Morpheus::CooMatrix<T, P...>& coo, Stream& input,
103 const matrix_market_banner& banner) {
104 using size_type = typename Morpheus::CooMatrix<T, P...>::size_type;
105 // read file contents line by line
106 std::string line;
107
108 // skip over banner and comments
109 do {
110 std::getline(input, line);
111 } while (line[0] == '%');
112
113 // line contains [num_rows num_columns num_entries]
114 std::vector<std::string> tokens;
115 Impl::tokenize(tokens, line);
116
117 if (tokens.size() != 3)
118 throw Morpheus::IOException("invalid MatrixMarket coordinate format");
119
120 size_type num_rows, num_cols, num_entries;
121
122 std::istringstream(tokens[0]) >> num_rows;
123 std::istringstream(tokens[1]) >> num_cols;
124 std::istringstream(tokens[2]) >> num_entries;
125
126 coo.resize(num_rows, num_cols, num_entries);
127
128 size_type num_entries_read = 0;
129
130 // read file contents
131 if (banner.type == "pattern") {
132 while (num_entries_read < coo.nnnz() && !input.eof()) {
133 input >> coo.row_indices(num_entries_read);
134 input >> coo.column_indices(num_entries_read);
135 num_entries_read++;
136 }
137
138 auto values_begin = coo.values().data();
139 auto values_end = coo.values().data() + coo.values().size();
140 std::fill(values_begin, values_end, T(1));
141 } else if (banner.type == "real" || banner.type == "integer") {
142 while (num_entries_read < coo.nnnz() && !input.eof()) {
143 T real;
144
145 input >> coo.row_indices(num_entries_read);
146 input >> coo.column_indices(num_entries_read);
147 input >> real;
148
149 coo.values(num_entries_read) = real;
150 num_entries_read++;
151 }
152 } else if (banner.type == "complex") {
154 "Morpheus does not currently support complex matrices");
155
156 } else {
157 throw Morpheus::IOException("invalid MatrixMarket data type");
158 }
159
160 if (num_entries_read != coo.nnnz())
162 "unexpected EOF while reading MatrixMarket entries");
163
164 // check validity of row and column index data
165 if (coo.nnnz() > 0) {
166 auto row_indices_begin = coo.row_indices().data();
167 auto row_indices_end = coo.row_indices().data() + coo.row_indices().size();
168 size_type min_row_index =
169 *std::min_element(row_indices_begin, row_indices_end);
170 size_type max_row_index =
171 *std::max_element(row_indices_begin, row_indices_end);
172
173 auto column_indices_begin = coo.column_indices().data();
174 auto column_indices_end =
175 coo.column_indices().data() + coo.column_indices().size();
176 size_type min_col_index =
177 *std::min_element(column_indices_begin, column_indices_end);
178 size_type max_col_index =
179 *std::max_element(column_indices_begin, column_indices_end);
180
181 if (min_row_index < 1)
182 throw Morpheus::IOException("found invalid row index (index < 1)");
183 if (min_col_index < 1)
184 throw Morpheus::IOException("found invalid column index (index < 1)");
185 if (max_row_index > coo.nrows())
186 throw Morpheus::IOException("found invalid row index (index > num_rows)");
187 if (max_col_index > coo.ncols())
189 "found invalid column index (index > num_columns)");
190 }
191
192 // convert base-1 indices to base-0
193 for (size_type n = 0; n < coo.nnnz(); n++) {
194 coo.row_indices(n) -= 1;
195 coo.column_indices(n) -= 1;
196 }
197
198 // expand symmetric formats to "general" format
199 if (banner.symmetry != "general") {
200 size_type off_diagonals = 0;
201
202 for (size_type n = 0; n < coo.nnnz(); n++)
203 if (coo.row_indices(n) != coo.column_indices(n)) off_diagonals++;
204
205 size_type general_num_entries = coo.nnnz() + off_diagonals;
206
207 Morpheus::CooMatrix<T, P...> general(num_rows, num_cols,
208 general_num_entries);
209
210 if (banner.symmetry == "symmetric") {
211 size_type nnz = 0;
212
213 for (size_type n = 0; n < coo.nnnz(); n++) {
214 // copy entry over
215 general.row_indices(nnz) = coo.row_indices(n);
216 general.column_indices(nnz) = coo.column_indices(n);
217 general.values(nnz) = coo.values(n);
218 nnz++;
219
220 // duplicate off-diagonals
221 if (coo.row_indices(n) != coo.column_indices(n)) {
222 general.row_indices(nnz) = coo.column_indices(n);
223 general.column_indices(nnz) = coo.row_indices(n);
224 general.values(nnz) = coo.values(n);
225 nnz++;
226 }
227 }
228 } else if (banner.symmetry == "hermitian") {
230 "MatrixMarket I/O does not currently support hermitian matrices");
231 // TODO
232 } else if (banner.symmetry == "skew-symmetric") {
234 "MatrixMarket I/O does not currently support skew-symmetric "
235 "matrices");
236 // TODO
237 }
238
239 // store full matrix in coo
240 coo = general;
241 } // if (banner.symmetry != "general")
242
243#if defined(MORPHEUS_ENABLE_SERIAL)
244 // sort indices by (row,column)
245 Morpheus::sort_by_row_and_column<Morpheus::Serial>(coo);
246#else
247 Morpheus::sort_by_row_and_column<Morpheus::DefaultHostSpace>(coo);
248#endif
249}
250
251template <typename T, typename... P, typename Stream>
252void read_array_stream(Morpheus::DenseMatrix<T, P...>& mtx, Stream& input,
253 const matrix_market_banner& banner) {
254 using size_type = typename Morpheus::DenseMatrix<T, P...>::size_type;
255
256 // read file contents line by line
257 std::string line;
258
259 // skip over banner and comments
260 do {
261 std::getline(input, line);
262 } while (line[0] == '%');
263
264 std::vector<std::string> tokens;
265 Impl::tokenize(tokens, line);
266
267 if (tokens.size() != 2)
268 throw Morpheus::IOException("invalid MatrixMarket array format");
269
270 size_type num_rows, num_cols;
271
272 std::istringstream(tokens[0]) >> num_rows;
273 std::istringstream(tokens[1]) >> num_cols;
274
275 Morpheus::DenseMatrix<T, P...> dense(num_rows, num_cols);
276
277 size_type num_entries = num_rows * num_cols;
278 size_type num_entries_read = 0;
279 size_type nrows = 0, ncols = 0;
280
281 // read file contents
282 if (banner.type == "pattern") {
284 "pattern array MatrixMarket format is not supported");
285 } else if (banner.type == "real" || banner.type == "integer") {
286 while (num_entries_read < num_entries && !input.eof()) {
287 double real;
288
289 input >> real;
290
291 dense(nrows, ncols) = T(real);
292
293 num_entries_read++;
294 ncols++;
295 if (ncols % num_cols == 0) {
296 nrows++;
297 ncols = 0;
298 }
299 }
300 } else if (banner.type == "complex") {
301 throw Morpheus::NotImplementedException("complex type is not supported");
302 } else {
303 throw Morpheus::IOException("invalid MatrixMarket data type");
304 }
305
306 if (num_entries_read != num_entries)
308 "unexpected EOF while reading MatrixMarket entries");
309
310 if (banner.symmetry != "general")
312 "only general array symmetric MatrixMarket format is supported");
313
314 mtx = dense;
315}
316
317template <template <class, class...> class Container, class T, class... P,
318 typename Stream>
319void read_matrix_market_stream(
320 Container<T, P...>& mtx, Stream& input,
321 typename std::enable_if<
322 is_coo_matrix_format_container_v<Container<T, P...>> &&
323 has_host_memory_space_v<Container<T, P...>>>::type* = nullptr) {
324 matrix_market_banner banner;
325 read_matrix_market_banner(banner, input);
326
327 if (banner.storage == "coordinate") {
328 Morpheus::CooMatrix<T, P...> temp;
329 read_coordinate_stream(temp, input, banner);
330 mtx = temp;
331 } else {
333 "only coordinate storage format is supported for reading a "
334 "MatrixMarket file into CooMatrix container");
335 }
336}
337
338template <template <class, class...> class Container, class T, class... P,
339 typename Stream>
340void read_matrix_market_stream(
341 Container<T, P...>& mtx, Stream& input,
342 typename std::enable_if<
343 is_dynamic_matrix_format_container_v<Container<T, P...>> &&
344 has_host_memory_space_v<Container<T, P...>>>::type* = nullptr) {
345 matrix_market_banner banner;
346 read_matrix_market_banner(banner, input);
347
348 if (banner.storage == "coordinate") {
349 if (mtx.active_index() != Morpheus::COO_FORMAT) {
351 "read_matrix_market_stream: The active state of a DynamicMatrix must "
352 "be set to Morpheus::COO_FORMAT before reading a MatrixMarket "
353 "stream!");
354 }
355
356 Morpheus::CooMatrix<T, P...> temp;
357 read_coordinate_stream(temp, input, banner);
358 mtx = temp;
359 } else {
361 "only coordinate storage format is supported for reading a "
362 "MatrixMarket file into CooMatrix container");
363 }
364}
365
366template <template <class, class...> class Container, class T, class... P,
367 typename Stream>
368void read_matrix_market_stream(
369 Container<T, P...>& container, Stream& input,
370 typename std::enable_if<
371 is_dense_matrix_format_container_v<Container<T, P...>> &&
372 has_host_memory_space_v<Container<T, P...>>>::type* = nullptr) {
373 matrix_market_banner banner;
374 read_matrix_market_banner(banner, input);
375
376 if (banner.storage == "array") {
377 Morpheus::DenseMatrix<T, P...> temp;
378 Impl::read_array_stream(temp, input, banner);
379
380 container = temp;
381 } else {
383 "only coordinate storage format is supported for reading a "
384 "MatrixMarket file into DenseMatrix container");
385 }
386}
387
388template <template <class, class...> class Container, class T, class... P,
389 typename Stream>
390void read_matrix_market_stream(
391 Container<T, P...>& container, Stream& input,
392 typename std::enable_if<
393 is_dense_vector_format_container_v<Container<T, P...>> &&
394 has_host_memory_space_v<Container<T, P...>>>::type* = nullptr) {
395 // read banner
396 matrix_market_banner banner;
397 read_matrix_market_banner(banner, input);
398
399 if (banner.storage == "array") {
400 Morpheus::DenseMatrix<T, P...> temp;
401 Impl::read_array_stream(temp, input, banner);
402
403 Morpheus::convert<Morpheus::Serial>(temp, container);
404 } else {
406 "only coordinate storage format is supported for reading a "
407 "MatrixMarket file into DenseVector container");
408 }
409}
410
411template <typename Stream, typename ScalarType>
412void write_value(Stream& output, const ScalarType& value) {
413 output << value;
414}
415
416template <typename T, typename... P, typename Stream>
417void write_coordinate_stream(const Morpheus::CooMatrix<T, P...>& coo,
418 Stream& output) {
419 using size_type = typename Morpheus::CooMatrix<T, P...>::size_type;
420
421 if (std::is_floating_point_v<T> || std::is_integral_v<T>) {
422 output << "%%MatrixMarket matrix coordinate real general\n";
423 } else {
424 throw Morpheus::NotImplementedException("complex type is not supported.");
425 }
426
427 output << coo.nrows() << "\t" << coo.ncols() << "\t" << coo.nnnz() << "\n";
428
429 for (size_type i = 0; i < coo.nnnz(); i++) {
430 output << (coo.crow_indices(i) + 1) << " ";
431 output << (coo.ccolumn_indices(i) + 1) << " ";
432 Impl::write_value(output, coo.cvalues(i));
433 output << "\n";
434 }
435}
436
437template <template <class, class...> class Container, class T, class... P,
438 typename Stream>
439void write_matrix_market_stream(
440 const Container<T, P...>& mtx, Stream& output,
441 typename std::enable_if<
442 is_coo_matrix_format_container_v<Container<T, P...>> &&
443 has_host_memory_space_v<Container<T, P...>>>::type* = nullptr) {
444 Impl::write_coordinate_stream(mtx, output);
445}
446
447template <template <class, class...> class Container, class T, class... P,
448 typename Stream>
449void write_matrix_market_stream(
450 const Container<T, P...>& mtx, Stream& output,
451 typename std::enable_if<
452 is_dynamic_matrix_format_container_v<Container<T, P...>> &&
453 has_host_memory_space_v<Container<T, P...>>>::type* = nullptr) {
454 if (mtx.active_index() != Morpheus::COO_FORMAT) {
456 "write_matrix_market_stream: The active state of a DynamicMatrix must "
457 "be set to Morpheus::COO_FORMAT before writing a MatrixMarket stream!");
458 }
459 Morpheus::CooMatrix<T, P...> coo = mtx;
460 Impl::write_coordinate_stream(coo, output);
461}
462
463template <template <class, class...> class Container, class T, class... P,
464 typename Stream>
465void write_matrix_market_stream(
466 const Container<T, P...>& vec, Stream& output,
467 typename std::enable_if<
468 is_dense_vector_format_container_v<Container<T, P...>> &&
469 has_host_memory_space_v<Container<T, P...>>>::type* = nullptr) {
470 using size_type = typename Container<T, P...>::size_type;
471
472 if (std::is_floating_point_v<T> || std::is_integral_v<T>) {
473 output << "%%MatrixMarket matrix array real general\n";
474 } else {
475 throw Morpheus::NotImplementedException("complex type is not supported.");
476 }
477
478 output << vec.size() << "\t1\n";
479
480 for (size_type i = 0; i < vec.size(); i++) {
481 Impl::write_value(output, vec[i]);
482 output << "\n";
483 }
484}
485
486template <template <class, class...> class Container, class T, class... P,
487 typename Stream>
488void write_matrix_market_stream(
489 const Container<T, P...>& mtx, Stream& output,
490 typename std::enable_if<
491 is_dense_matrix_format_container_v<Container<T, P...>> &&
492 has_host_memory_space_v<Container<T, P...>>>::type* = nullptr) {
493 using size_type = typename Container<T, P...>::size_type;
494
495 if (std::is_floating_point_v<T> || std::is_integral_v<T>) {
496 output << "%%MatrixMarket matrix array real general\n";
497 } else {
498 throw Morpheus::NotImplementedException("complex type is not supported.");
499 }
500
501 output << mtx.nrows() << "\t" << mtx.ncols() << "\n";
502
503 for (size_type i = 0; i < (size_t)mtx.nrows(); i++) {
504 for (size_type j = 0; j < (size_t)mtx.ncols(); j++) {
505 Impl::write_value(output, mtx(i, j));
506 output << "\n";
507 }
508 }
509}
510
511} // namespace Impl
512} // namespace IO
513} // namespace Morpheus
514
515#endif // MORPHEUS_MATRIX_MARKET_IMPL_HPP
Implementation of the Coordinate (COO) Sparse Matrix Format Representation.
Definition: Morpheus_CooMatrix.hpp:91
MORPHEUS_FORCEINLINE_FUNCTION const index_array_reference ccolumn_indices(size_type n) const
Returns a const-reference to the column index of the matrix with index n.
Definition: Morpheus_CooMatrix.hpp:495
MORPHEUS_FORCEINLINE_FUNCTION index_array_reference column_indices(size_type n)
Returns a reference to the column index of the matrix with index n.
Definition: Morpheus_CooMatrix.hpp:461
MORPHEUS_FORCEINLINE_FUNCTION const value_array_reference cvalues(size_type n) const
Returns a const-reference to the value of the matrix with index n.
Definition: Morpheus_CooMatrix.hpp:506
MORPHEUS_FORCEINLINE_FUNCTION const index_array_reference crow_indices(size_type n) const
Returns a const-reference to the row index of the matrix with index n.
Definition: Morpheus_CooMatrix.hpp:483
void resize(const size_type num_rows, const size_type num_cols, const size_type num_entries)
Resizes CooMatrix with shape of (num_rows, num_cols) and sets number of non-zero entries to num_entri...
Definition: Morpheus_CooMatrix.hpp:363
MORPHEUS_FORCEINLINE_FUNCTION value_array_reference values(size_type n)
Returns a reference to the value of the matrix with index n.
Definition: Morpheus_CooMatrix.hpp:471
MORPHEUS_FORCEINLINE_FUNCTION index_array_reference row_indices(size_type n)
Returns a reference to the row index of the matrix with index n.
Definition: Morpheus_CooMatrix.hpp:449
The DenseMatrix container is a two-dimensional dense container that contains contiguous elements....
Definition: Morpheus_DenseMatrix.hpp:74
Definition: Morpheus_Exceptions.hpp:51
size_type nnnz() const
Number of non-zeros of the matrix.
Definition: Morpheus_MatrixBase.hpp:133
size_type nrows() const
Number of rows of the matrix.
Definition: Morpheus_MatrixBase.hpp:119
size_type ncols() const
Number of columns of the matrix.
Definition: Morpheus_MatrixBase.hpp:126
Definition: Morpheus_Exceptions.hpp:43
Definition: Morpheus_Exceptions.hpp:57
constexpr bool is_dense_vector_format_container_v
Short-hand to is_dense_vector_format_container.
Definition: Morpheus_FormatTags.hpp:298
constexpr bool is_dense_matrix_format_container_v
Short-hand to is_dense_matrix_format_container.
Definition: Morpheus_FormatTags.hpp:262
constexpr bool is_dynamic_matrix_format_container_v
Short-hand to is_dynamic_matrix_format_container.
Definition: Morpheus_FormatTags.hpp:226
constexpr bool is_coo_matrix_format_container_v
Short-hand to is_coo_matrix_format_container.
Definition: Morpheus_FormatTags.hpp:120
Generic Morpheus interfaces.
Definition: dummy.cpp:24
constexpr bool has_host_memory_space_v
Short-hand to has_host_memory_space.
Definition: Morpheus_SpaceTraits.hpp:314
Definition: Morpheus_MatrixMarket_Impl.hpp:61