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