NVBIO
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
sparsematrix.cpp
Go to the documentation of this file.
1 /******************************************************************************
2  * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  * * Redistributions of source code must retain the above copyright
7  * notice, this list of conditions and the following disclaimer.
8  * * Redistributions in binary form must reproduce the above copyright
9  * notice, this list of conditions and the following disclaimer in the
10  * documentation and/or other materials provided with the distribution.
11  * * Neither the name of the NVIDIA CORPORATION nor the
12  * names of its contributors may be used to endorse or promote products
13  * derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  ******************************************************************************/
27 
28 /******************************************************************************
29  *
30  * Code and text by Sean Baxter, NVIDIA Research
31  * See http://nvlabs.github.io/moderngpu for repository and documentation.
32  *
33  ******************************************************************************/
34 
35 #include <algorithm>
36 #include <sstream>
37 #include <map>
38 #include "sparsematrix.h"
39 #include "mmio.h"
40 
41 namespace mgpu {
42 
43 struct MatrixElement {
44  int row, col;
45  double value;
46  bool operator<(const MatrixElement& b) const {
47  if(row < b.row) return true;
48  if(b.row < row) return false;
49  return col < b.col;
50  }
51 };
52 
53 bool ReadSparseMatrix(FILE* f, std::auto_ptr<SparseMatrix>* ppMatrix,
54  std::string& err) {
55 
56  MM_typecode matcode;
57  int code = mm_read_banner(f, &matcode);
58  if(code) {
59  err = "Not a sparse matrix.\n";
60  return false;
61  }
62 
63  bool isPattern = mm_is_pattern(matcode);
64  bool isReal = mm_is_real(matcode);
65 
66  if(!(isPattern || isReal) || !mm_is_matrix(matcode) ||
67  !mm_is_coordinate(matcode) || !mm_is_sparse(matcode)) {
68  err = "Not a real matrix.\n";
69  return false;
70  }
71 
72  int nz;
73  int height, width;
74  code = mm_read_mtx_crd_size(f, &height, &width, &nz);
75  std::vector<MatrixElement> elements;
76  elements.reserve(nz);
77 
78  for(int i = 0; i < nz; ++i) {
79  MatrixElement e;
80  int x, y;
81 
82  if(isReal) {
83  int count = fscanf(f, "%d %d %lf", &y, &x, &e.value);
84  if(3 != count) {
85  std::ostringstream oss;
86  oss<< "Error parsing line "<< (i + 1);
87  err = oss.str();
88  return false;
89  }
90  } else if(isPattern) {
91  int count = fscanf(f, "%d %df", &y, &x);
92  if(2 != count) {
93  std::ostringstream oss;
94  oss<< "Error parsing line "<< (i + 1);
95  err = oss.str();
96  return false;
97  }
98  e.value = 1;
99  }
100  e.row = y - 1;
101  e.col = x - 1;
102  elements.push_back(e);
103 
104  if((mm_is_symmetric(matcode) || mm_is_skew(matcode)) && (x != y)) {
105  std::swap(e.row, e.col);
106  if(mm_is_skew(matcode)) e.value = -e.value;
107  elements.push_back(e);
108  }
109  }
110 
111  std::sort(elements.begin(), elements.end());
112 
113  std::auto_ptr<SparseMatrix> m(new SparseMatrix);
114 
115  nz = (int)elements.size();
116  m->height = height;
117  m->width = width;
118  m->nz = nz;
119  m->csr.resize(height + 1);
120  m->cols.resize(nz);
121  m->matrix.resize(nz);
122  for(int i = 0; i < nz; ++i) {
123  MatrixElement e = elements[i];
124  ++m->csr[e.row];
125  m->cols[i] = e.col;
126  m->matrix[i] = e.value;
127  }
128 
129  // scan the counts into CSR.
130  int scan = 0;
131  for(int i = 0; i < height; ++i) {
132  int count = m->csr[i];
133  m->csr[i] = scan;
134  scan += count;
135  }
136  m->csr[height] = scan;
137 
138  *ppMatrix = m;
139 
140  return true;
141 }
142 
143 bool ReadSparseMatrix(const char* filename,
144  std::auto_ptr<SparseMatrix>* ppMatrix, std::string& err) {
145 
146  FILE* f = fopen(filename, "r");
147  if(!f) {
148  err = "Could not open file.";
149  return false;
150  }
151 
152  bool success = ReadSparseMatrix(f, ppMatrix, err);
153 
154  fclose(f);
155  return success;
156 }
157 
158 bool LoadBinaryMatrix(const char* filename,
159  std::auto_ptr<SparseMatrix>* ppMatrix) {
160 
161  FILE* f = fopen(filename, "rb");
162  if(!f) return false;
163 
164  std::auto_ptr<SparseMatrix> m(new SparseMatrix);
165  fread(&m->height, 4, 1, f);
166  fread(&m->width, 4, 1, f);
167  fread(&m->nz, 4, 1, f);
168 
169  m->csr.resize(m->height + 1);
170  m->cols.resize(m->nz);
171  m->matrix.resize(m->nz);
172 
173  fread(&m->csr[0], 4, m->height + 1, f);
174  fread(&m->cols[0], 4, m->nz, f);
175  fread(&m->matrix[0], 8, m->nz, f);
176 
177  fclose(f);
178 
179  *ppMatrix = m;
180  return true;
181 }
182 
183 bool StoreBinaryMatrix(const char* filename, const SparseMatrix& m) {
184 
185  FILE* f = fopen(filename, "wb");
186  if(!f) return false;
187 
188  fwrite(&m.height, 4, 1, f);
189  fwrite(&m.width, 4, 1, f);
190  fwrite(&m.nz, 4, 1, f);
191 
192  fwrite(&m.csr[0], 4, m.height + 1, f);
193  fwrite(&m.cols[0], 4, m.nz, f);
194  fwrite(&m.matrix[0], 8, m.nz, f);
195 
196  fclose(f);
197  return true;
198 }
199 
200 bool LoadCachedMatrix(const char* filename,
201  std::auto_ptr<SparseMatrix>* ppMatrix, std::string& err) {
202 
203  // Attempt to load the binary matrix. If that fails, load the Matrix Market
204  // ASCII matrix and store as binary.
205  std::auto_ptr<SparseMatrix> m;
206  char s[260];
207  sprintf(s, "%s.bin", filename);
208  bool success = LoadBinaryMatrix(s, &m);
209  if(!success) {
210  success = ReadSparseMatrix(filename, &m, err);
211  if(!success) return false;
212 
213  printf("Creating temp file %s...\n", s);
214  StoreBinaryMatrix(s, *m);
215  }
216  *ppMatrix = m;
217  return true;
218 }
219 
221 
224  stats.height = m.height;
225  stats.width = m.width;
226  stats.nz = m.nz;
227  stats.mean = (double)m.nz / m.height;
228 
229  // Compute the second moment.
230  double variance = 0;
231  for(int i = 0; i < m.height; ++i) {
232  int count = m.csr[i + 1] - m.csr[i];
233  double x = count - stats.mean;
234  variance += x * x;
235  }
236  stats.stddev = sqrt(variance / m.height);
237 
238  // Compute the third moment.
239  double skewness = 0;
240  for(int i = 0; i < m.height; ++i) {
241  int count = m.csr[i + 1] - m.csr[i];
242  double x = count - stats.mean;
243  skewness += x * x * x;
244  }
245  stats.skewness = skewness / m.height / pow(stats.stddev, 3.0);
246 
247  return stats;
248 }
249 
251 // MulSparseMatrices
252 
254  std::auto_ptr<SparseMatrix>* ppC) {
255 
256  std::auto_ptr<SparseMatrix> C(new SparseMatrix);
257  C->height = A.height;
258  C->width = B.width;
259  C->nz = 0;
260 
261  int64 numProducts = 0;
262  for(int row = 0; row < A.height; ++row) {
263  std::map<int, double> rowMap;
264  int aCsr0 = A.csr[row];
265  int aCsr1 = (row + 1 < A.height) ? A.csr[row + 1] : A.nz;
266 
267  for(int i = aCsr0; i < aCsr1; ++i) {
268  int aCol = A.cols[i];
269  double x = A.matrix[i];
270 
271  int bCsr0 = B.csr[aCol];
272  int bCsr1 = (aCol + 1 < B.height) ? B.csr[aCol + 1] : B.nz;
273 
274  numProducts += bCsr1 - bCsr0;
275 
276  for(int j = bCsr0; j < bCsr1; ++j)
277  rowMap[B.cols[j]] += x * B.matrix[j];
278  }
279 
280  // Read out the row data.
281  std::map<int, double>::const_iterator i = rowMap.begin();
282  C->csr.push_back(C->nz);
283  while(i != rowMap.end()) {
284  C->cols.push_back(i->first);
285  C->matrix.push_back(i->second);
286  ++i;
287  }
288  C->nz += (int)rowMap.size();
289  }
290 
291  *ppC = C;
292  return numProducts;
293 }
294 
295 
297  int64 numProducts = 0;
298  for(int ai = 0; ai < A.nz; ++ai) {
299  int aCol = A.cols[ai];
300  int bCsr0 = B.csr[aCol];
301  int bCsr1 = (aCol + 1 < B.width) ? B.csr[aCol + 1] : B.nz;
302  numProducts += bCsr1 - bCsr0;
303  }
304  return numProducts;
305 }
306 
308  int* colMin, int* colMax) {
309 
310  // Loop over all NZ elements in A and do two lookups into B.
311  for(int row = 0; row < A.height; ++row) {
312  int cMin = B.width;
313  int cMax = 0;
314 
315  int csr0 = A.csr[row];
316  int csr1 = A.csr[row + 1];
317  for(int i = csr0; i < csr1; ++i) {
318  int aCol = A.cols[i];
319  cMin = std::min(cMin, B.cols[B.csr[aCol]]);
320  cMax = std::max(cMax, B.cols[B.csr[aCol + 1] - 1]);
321  }
322  colMin[row] = cMin;
323  colMax[row] = cMax;
324  }
325 }
326 
327 } // namespace mgpu