1+ #pragma once
2+
3+ #include < iostream>
4+ #include < fstream>
5+ #include < ranges>
6+
7+ namespace binsparse {
8+
9+ namespace __detail {
10+
11+ template <typename T, typename I>
12+ class csr_matrix_owning {
13+ public:
14+
15+ csr_matrix_owning (std::tuple<I, I> shape) : shape_(shape) {}
16+
17+ auto values () { return std::ranges::views::all (values_); }
18+ auto rowptr () { return std::ranges::views::all (rowptr_); }
19+ auto colind () { return std::ranges::views::all (colind_); }
20+
21+ auto values () const { return std::ranges::views::all (values_); }
22+ auto rowptr () const { return std::ranges::views::all (rowptr_); }
23+ auto colind () const { return std::ranges::views::all (colind_); }
24+
25+ template <typename Iter>
26+ void assign_tuples (Iter first, Iter last) {
27+ std::size_t nnz = std::ranges::distance (first, last);
28+ values_.resize (nnz);
29+ colind_.resize (nnz);
30+ rowptr_.resize (std::get<0 >(shape ())+1 );
31+
32+ rowptr_[0 ] = 0 ;
33+
34+ std::size_t r = 0 ;
35+ std::size_t c = 0 ;
36+ for (auto iter = first; iter != last; ++iter) {
37+ auto && [index, value] = *iter;
38+ auto && [i, j] = index;
39+
40+ values_[c] = value;
41+ colind_[c] = j;
42+
43+ while (r < i) {
44+ if (r+1 > std::get<0 >(shape ())) {
45+ // TODO: exception?
46+ // throw std::runtime_error("csr_matrix_impl_: given invalid matrix");
47+ }
48+ rowptr_[r+1 ] = c;
49+ r++;
50+ }
51+ c++;
52+
53+ if (c > nnz) {
54+ // TODO: exception?
55+ // throw std::runtime_error("csr_matrix_impl_: given invalid matrix");
56+ }
57+ }
58+
59+ for ( ; r < std::get<0 >(shape ()); r++) {
60+ rowptr_[r+1 ] = nnz;
61+ }
62+ }
63+
64+ auto shape () const {
65+ return shape_;
66+ }
67+
68+ auto size () const {
69+ return values_.size ();
70+ }
71+
72+ private:
73+ std::tuple<I, I> shape_;
74+ std::vector<T> values_;
75+ std::vector<I> rowptr_;
76+ std::vector<I> colind_;
77+ };
78+
79+ template <typename T, typename I>
80+ class coo_matrix_owning {
81+ public:
82+
83+ coo_matrix_owning (std::tuple<I, I> shape) : shape_(shape) {}
84+
85+ auto values () { return std::ranges::views::all (values_); }
86+ auto rowind () { return std::ranges::views::all (rowind_); }
87+ auto colind () { return std::ranges::views::all (colind_); }
88+
89+ auto values () const { return std::ranges::views::all (values_); }
90+ auto rowind () const { return std::ranges::views::all (rowind_); }
91+ auto colind () const { return std::ranges::views::all (colind_); }
92+
93+ void push_back (std::tuple<std::tuple<I, I>, T> entry) {
94+ auto && [idx, v] = entry;
95+ auto && [i, j] = idx;
96+ values_.push_back (v);
97+ rowind_.push_back (i);
98+ colind_.push_back (j);
99+ }
100+
101+ template <typename Iter>
102+ void assign_tuples (Iter first, Iter last) {
103+ std::size_t nnz = std::ranges::distance (first, last);
104+ for (auto iter = first; iter != last; ++iter) {
105+ auto && [idx, v] = *iter;
106+ auto && [i, j] = idx;
107+ push_back ({{i, j}, v});
108+ }
109+ }
110+
111+ void reserve (std::size_t size) {
112+ values_.reserve (size);
113+ rowind_.reserve (size);
114+ colind_.reserve (size);
115+ }
116+
117+ auto shape () const {
118+ return shape_;
119+ }
120+
121+ auto size () const {
122+ return values_.size ();
123+ }
124+
125+ private:
126+ std::tuple<I, I> shape_;
127+ std::vector<T> values_;
128+ std::vector<I> rowind_;
129+ std::vector<I> colind_;
130+ };
131+
132+ // / Read in the Matrix Market file at location `file_path` and
133+ // / return a data structure with the matrix.
134+ template <typename T, typename I, typename MatrixType>
135+ inline MatrixType mmread (std::string file_path, bool one_indexed = true ) {
136+ using index_type = I;
137+ using size_type = std::size_t ;
138+
139+ std::ifstream f;
140+
141+ f.open (file_path.c_str ());
142+
143+ if (!f.is_open ()) {
144+ // TODO better choice of exception.
145+ throw std::runtime_error (" mmread: cannot open " + file_path);
146+ }
147+
148+ std::string buf;
149+
150+ // Make sure the file is matrix market matrix, coordinate, and check whether
151+ // it is symmetric. If the matrix is symmetric, non-diagonal elements will
152+ // be inserted in both (i, j) and (j, i). Error out if skew-symmetric or
153+ // Hermitian.
154+ std::getline (f, buf);
155+ std::istringstream ss (buf);
156+ std::string item;
157+ ss >> item;
158+ if (item != " %%MatrixMarket" ) {
159+ throw std::runtime_error (file_path + " could not be parsed as a Matrix Market file." );
160+ }
161+ ss >> item;
162+ if (item != " matrix" ) {
163+ throw std::runtime_error (file_path + " could not be parsed as a Matrix Market file." );
164+ }
165+ ss >> item;
166+ if (item != " coordinate" ) {
167+ throw std::runtime_error (file_path + " could not be parsed as a Matrix Market file." );
168+ }
169+ bool pattern;
170+ ss >> item;
171+ if (item == " pattern" ) {
172+ pattern = true ;
173+ } else {
174+ pattern = false ;
175+ }
176+ // TODO: do something with real vs. integer vs. pattern?
177+ ss >> item;
178+ bool symmetric;
179+ if (item == " general" ) {
180+ symmetric = false ;
181+ } else if (item == " symmetric" ) {
182+ symmetric = true ;
183+ } else {
184+ throw std::runtime_error (file_path + " has an unsupported matrix type" );
185+ }
186+
187+ bool outOfComments = false ;
188+ while (!outOfComments) {
189+ std::getline (f, buf);
190+
191+ if (buf[0 ] != ' %' ) {
192+ outOfComments = true ;
193+ }
194+ }
195+
196+ I m, n, nnz;
197+ // std::istringstream ss(buf);
198+ ss.clear ();
199+ ss.str (buf);
200+ ss >> m >> n >> nnz;
201+
202+ // NOTE for symmetric matrices: `nnz` holds the number of stored values in
203+ // the matrix market file, while `matrix.nnz_` will hold the total number of
204+ // stored values (including "mirrored" symmetric values).
205+ MatrixType m_out ({m, n});
206+
207+ using coo_type = std::vector<std::tuple<std::tuple<I, I>, T>>;
208+ coo_type matrix;
209+ if (symmetric) {
210+ matrix.reserve (2 *nnz);
211+ } else {
212+ matrix.reserve (nnz);
213+ }
214+
215+ size_type c = 0 ;
216+ while (std::getline (f, buf)) {
217+ I i, j;
218+ T v;
219+ std::istringstream ss (buf);
220+ if (!pattern) {
221+ ss >> i >> j >> v;
222+ } else {
223+ ss >> i >> j;
224+ v = T (1 );
225+ }
226+ if (one_indexed) {
227+ i--;
228+ j--;
229+ }
230+
231+ if (i >= m || j >= n) {
232+ throw std::runtime_error (" read_MatrixMarket: file has nonzero out of bounds." );
233+ }
234+
235+ matrix.push_back ({{i, j}, v});
236+
237+ if (symmetric && i != j) {
238+ matrix.push_back ({{j, i}, v});
239+ }
240+
241+ c++;
242+ if (c > nnz) {
243+ throw std::runtime_error (" read_MatrixMarket: error reading Matrix Market file, file has more nonzeros than reported." );
244+ }
245+ }
246+
247+ auto sort_fn = [](auto && a, auto && b) {
248+ auto && [a_idx, a_v] = a;
249+ auto && [b_idx, b_v] = b;
250+
251+ auto && [a_i, a_j] = a_idx;
252+ auto && [b_i, b_j] = b_idx;
253+
254+ if (a_i != b_i) {
255+ return a_i < b_i;
256+ } else {
257+ return a_j < b_j;
258+ }
259+ };
260+
261+ std::sort (matrix.begin (), matrix.end (), sort_fn);
262+
263+ m_out.assign_tuples (matrix.begin (), matrix.end ());
264+
265+ f.close ();
266+
267+ return m_out;
268+ }
269+
270+ } // end __detail
271+
272+ } // end binsparse
0 commit comments