1 /*******************************************************************************
2 * PRIMME PReconditioned Iterative MultiMethod Eigensolver
3 * Copyright (C) 2018 College of William & Mary,
4 * James R. McCombs, Eloy Romero Alcalde, Andreas Stathopoulos, Lingfei Wu
5 *
6 * This file is part of PRIMME.
7 *
8 * PRIMME is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * PRIMME is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 *
22 *******************************************************************************
23 * File: csr.c
24 *
25 * Purpose - Functions to read MatrixMarket format matrices and other CSR
26 * auxiliary functions.
27 *
28 ******************************************************************************/
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <math.h>
35 #include "mmio.h"
36 #include "primme.h"
37 #include "csr.h"
38
39 static int readfullMTX(const char *mtfile, SCALAR **A, int **JA, int **IA, int *m, int *n, int *nnz);
40 #ifndef USE_DOUBLECOMPLEX
41 static int readUpperMTX(const char *mtfile, double **A, int **JA, int **IA, int *n, int *nnz);
42 int ssrcsr(int *job, int *value2, int *nrow, double *a, int *ja, int *ia,
43 int *nzmax, double *ao, int *jao, int *iao, int *indu, int *iwk, int *ierr);
44 #endif
45
readMatrixNative(const char * matrixFileName,CSRMatrix ** matrix_,double * fnorm)46 int readMatrixNative(const char* matrixFileName, CSRMatrix **matrix_, double *fnorm) {
47 int ret;
48 CSRMatrix *matrix;
49
50 matrix = (CSRMatrix*)primme_calloc(1, sizeof(CSRMatrix), "CSRMatrix");
51 if (!strcmp("mtx", &matrixFileName[strlen(matrixFileName)-3])) {
52 /* coordinate format storing both lower and upper triangular parts */
53 ret = readfullMTX(matrixFileName, &matrix->AElts, &matrix->JA,
54 &matrix->IA, &matrix->m, &matrix->n, &matrix->nnz);
55 if (ret < 0) {
56 fprintf(stderr, "ERROR: Could not read matrix file\n");
57 return(-1);
58 }
59 }
60 else if (matrixFileName[strlen(matrixFileName)-1] == 'U') {
61 /* coordinate format storing only upper triangular part */
62 #ifndef USE_DOUBLECOMPLEX
63 ret = readUpperMTX(matrixFileName, &matrix->AElts, &matrix->JA,
64 &matrix->IA, &matrix->n, &matrix->nnz);
65 #else
66 /* TODO: support this in complex arithmetic */
67 ret = -1;
68 #endif
69 if (ret < 0) {
70 fprintf(stderr, "ERROR: Could not read matrix file\n");
71 return(-1);
72 }
73 }
74 else {
75 /* Harwell Boeing format NOT IMPLEMENTED */
76 /* ret = readmt() */
77 ret = -1;
78 if (ret < 0) {
79 fprintf(stderr, "ERROR: Could not read matrix file\n");
80 return(-1);
81 }
82 }
83 *matrix_ = matrix;
84 if (fnorm)
85 *fnorm = frobeniusNorm(matrix);
86
87 return 0;
88 }
89
90 static int *my_comp_ctx[2];
my_comp(const void * a,const void * b)91 static int my_comp(const void *a, const void *b)
92 {
93 const int ia = *(int*)a, ib = *(int*)b;
94 int **p = my_comp_ctx;
95 return p[0][ia] != p[0][ib] ? p[0][ia] - p[0][ib] : p[1][ia] - p[1][ib];
96 }
97
readfullMTX(const char * mtfile,SCALAR ** AA,int ** JA,int ** IA,int * m,int * n,int * nnz)98 static int readfullMTX(const char *mtfile, SCALAR **AA, int **JA, int **IA, int *m, int *n, int *nnz) {
99 int i,j, k, nzmax;
100 int *I, *J, *perm;
101 SCALAR *A;
102 double re, im;
103 FILE *matrixFile;
104 MM_typecode type;
105
106 matrixFile = fopen(mtfile, "r");
107 if (matrixFile == NULL) {
108 return(-1);
109 }
110
111 /* first read to set matrix kind and size */
112 if(mm_read_banner(matrixFile, &type) != 0) return -1;
113 if (!mm_is_valid(type) || !mm_is_sparse(type) ||
114 #ifndef USE_DOUBLECOMPLEX
115 !(mm_is_real(type) || mm_is_pattern(type) || mm_is_integer(type))
116 #else
117 !(mm_is_real(type) || mm_is_complex(type) || mm_is_pattern(type) || mm_is_integer(type))
118 #endif
119 ) {
120 fprintf(stderr, "Matrix format '%s' not supported!", mm_typecode_to_str(type));
121 return -1;
122 }
123
124 if (mm_read_mtx_crd_size(matrixFile, m, n, nnz) != 0) return -1;
125
126 nzmax = *nnz;
127 if (mm_is_symmetric(type) || mm_is_hermitian(type) || mm_is_skew(type)) nzmax *= 2;
128 A = (SCALAR *)primme_calloc(nzmax, sizeof(SCALAR), "A");
129 J = (int *)primme_calloc(nzmax, sizeof(int), "J");
130 I = (int *)primme_calloc(nzmax, sizeof(int), "I");
131
132 /* Read matrix in COO */
133 im = 0.0;
134 for (k=0, i=0; k<*nnz; k++, i++) {
135 if (mm_read_mtx_crd_entry(matrixFile, &I[i], &J[i], &re, &im, type)!=0) return -1;
136 if (mm_is_pattern(type)) A[i] = 1;
137 else if (mm_is_real(type)) A[i] = re;
138 else A[i] = re + IMAGINARY*im;
139 if (I[i] != J[i]) {
140 if ((mm_is_symmetric(type) || mm_is_hermitian(type))) {
141 I[i+1] = J[i]; J[i+1] = I[i]; A[i+1] = CONJ(A[i]); i++;
142 }
143 else if (mm_is_skew(type)) {
144 I[i+1] = J[i]; J[i+1] = I[i]; A[i+1] = -A[i]; i++;
145 }
146 }
147 }
148 nzmax = *nnz = i;
149
150 /* Sort COO by columns */
151 perm = (int *)primme_calloc(nzmax, sizeof(int), "perm");
152 for (i=0; i<nzmax; i++) perm[i] = i;
153 my_comp_ctx[0] = I;
154 my_comp_ctx[1] = J;
155 qsort(perm, nzmax, sizeof(int), my_comp);
156
157 /* Collapse columns */
158 *IA = (int *)primme_calloc(*m+1, sizeof(int), "IA");
159 (*IA)[0] = 1;
160 for (i=0, j=1; i<nzmax; i++)
161 while (j < I[perm[i]]) (*IA)[j++] = i+1;
162 while (j <= *m) (*IA)[j++] = nzmax+1;
163
164 /* Copy rows and values sorted */
165 *JA = I;
166 for (i=0; i<nzmax; i++) (*JA)[i] = J[perm[i]];
167 free(J);
168 *AA = (SCALAR *)primme_calloc(nzmax, sizeof(SCALAR), "AA");
169 for (i=0; i<nzmax; i++) (*AA)[i] = A[perm[i]];
170 free(A);
171 free(perm);
172
173 fclose(matrixFile);
174
175 return 0;
176 }
177
178 #ifndef USE_DOUBLECOMPLEX
readUpperMTX(const char * mtfile,double ** A,int ** JA,int ** IA,int * n,int * nnz)179 static int readUpperMTX(const char *mtfile, double **A, int **JA, int **IA, int *n, int *nnz) {
180 int i, k, nzmax;
181 int job, value2;
182 int row, nextRow;
183 int ierror;
184 int *iwk1, *iwk2;
185 FILE *matrixFile;
186
187 matrixFile = fopen(mtfile, "r");
188
189 if (matrixFile == NULL) {
190 return(-1);
191 }
192
193 i = 0;
194 nextRow = 0;
195 if (fscanf(matrixFile, "%d %d\n", n, nnz) != 2) return -1;
196 fprintf(stderr, "%d %d\n", *n, *nnz);
197
198 nzmax = 2*(*nnz) - *n;
199 *A = (double *)primme_calloc(nzmax, sizeof(double), "A");
200 *JA = (int *)primme_calloc(nzmax, sizeof(int), "JA");
201 *IA = (int *)primme_calloc(*n+1, sizeof(int), "IA");
202
203 iwk1 = (int *)primme_calloc(*n+1, sizeof(int), "iwk1");
204 iwk2 = (int *)primme_calloc(*n+1, sizeof(int), "iwk2");
205
206 for (k=1; k <= *nnz; k++) {
207 int tja; double ta;
208 if (fscanf(matrixFile, "%d %d %lf\n", &row, &tja, &ta) != 3) return -1;
209 (*JA)[k-1]=tja;
210 (*A)[k-1] = ta;
211 if (i != row) {
212 i = row;
213 nextRow = nextRow + 1;
214 (*IA)[nextRow-1] = k;
215 }
216 }
217
218 (*IA)[*n] = (*IA)[0] + *nnz;
219 fclose(matrixFile);
220
221 job = 3;
222 value2 = 1;
223
224 ssrcsr(&job, &value2, n, *A, *JA, *IA, &nzmax, *A, *JA, *IA, iwk1, iwk2,
225 &ierror);
226 *nnz = 2*(*nnz) - *n;
227
228 free(iwk1);
229 free(iwk2);
230
231 return(0);
232 }
233 #endif
234
235 /******************************************************************************
236 * Computed the Frobenius norm of a CSR matrix
237 *
238 * ||A||_frob = sqrt( \sum_{i,j} A_ij^2 )
239 *
240 ******************************************************************************/
frobeniusNorm(const CSRMatrix * matrix)241 double frobeniusNorm(const CSRMatrix *matrix) {
242
243 int i, j;
244 double fnorm;
245
246 /* IA and JA are indexed using C indexing, but their contents */
247 /* assume Fortran indexing. Thus, the contents of IA and JA */
248 /* must be decremented before being used in C. */
249
250 fnorm = 0.0L;
251
252 for (i=0; i < matrix->m; i++) {
253 for (j=matrix->IA[i]; j <= matrix->IA[i+1]-1; j++) {
254 fnorm = fnorm + REAL_PART(CONJ(matrix->AElts[j-1])*matrix->AElts[j-1]);
255 }
256 }
257
258 return (sqrt(fnorm));
259 }
260
261 /******************************************************************************
262 * Shifts a CSR matrix by a shift
263 *
264 * A = A + shift I
265 *
266 ******************************************************************************/
shiftCSRMatrix(double shift,CSRMatrix * matrix)267 void shiftCSRMatrix(double shift, CSRMatrix *matrix) {
268
269 int i, j, n;
270
271 /* IA and JA are indexed using C indexing, but their contents */
272 /* assume Fortran indexing. Thus, the contents of IA and JA */
273 /* must be decremented before being used in C. */
274
275 for (i=0, n=min(matrix->m, matrix->n); i < n; i++) {
276 for (j=matrix->IA[i]; j <= matrix->IA[i+1]-1; j++) {
277
278 if (matrix->JA[j-1]-1 == i) {
279 matrix->AElts[j-1] = matrix->AElts[j-1] + shift;
280 }
281 }
282 }
283
284 }
285
freeCSRMatrix(CSRMatrix * matrix)286 void freeCSRMatrix(CSRMatrix *matrix) {
287 if (!matrix) return;
288 free(matrix->AElts);
289 free(matrix->IA);
290 free(matrix->JA);
291 free(matrix);
292 }
293