//------------------------------------------------------------------------------ // CHOLMOD/Check/cholmod_read: read a sparse matrix from a file //------------------------------------------------------------------------------ // CHOLMOD/Check Module. Copyright (C) 2005-2023, Timothy A. Davis // All Rights Reserved. // SPDX-License-Identifier: LGPL-2.1+ //------------------------------------------------------------------------------ // Read a sparse matrix in triplet or dense form. A triplet matrix can be // returned as compressed-column sparse matrix. The file format is compatible // with all variations of the Matrix Market "coordinate" and "array" format // (http://www.nist.gov/MatrixMarket). The format supported by these routines // also allow other formats, where the Matrix Market header is optional. // // Although the Matrix Market header is optional, I recommend that users stick // with the strict Matrix Market format. The optional format appears here to // support the reading of symmetric matrices stored with just their upper // triangular parts present, for testing and development of the A->stype > 0 // format in CHOLMOD. That format is not included in the Matrix Market format. // // If the first line of the file starts with %%MatrixMarket, then it is // interpretted as a file in Matrix Market format. This line must have // the following format: // // %%MatrixMarket matrix // // is one of: coordinate or array. The former is a sparse matrix in // triplet form. The latter is a dense matrix in column-major form. // // is one of: real, complex, pattern, or integer. // The functions here convert the "integer" and "pattern" types to real. // // is one of: general, hermitian, symmetric, or skew-symmetric // // The strings are case-insensitive. Only the first character is // significant (or the first two for skew-symmetric). // // is ignored for all matrices; the actual type (real, complex, // or pattern) is inferred from the number of tokens in each line of the // file. For a "coordinate" matrix: 2: pattern, 3: real, 4: complex; for // a dense "array" matrix: 1: real, 2: complex. This is compatible with // the Matrix Market format, since pattern matrices must have two tokens // per line, real matrices must have 3, and complex matrices must have 4. // A storage of "general" implies an stype of zero (see below). // "symmetric" and "hermitian" imply an stype of -1. Skew-symmetric and // complex symmetric matrices are always returned with both upper and lower // triangular parts present, with an stype of zero, since CHOLMOD does not // have a method for representing skew-symmetric and complex symmetric // matrices. real symmetric and complex Hermitian matrices may optionally // be returned with both parts present. // // Any other lines starting with "%" are treated as comments, and are ignored. // Blank lines are ignored. The Matrix Market header is optional in this // routine (it is not optional in the Matrix Market format). // // Note that complex matrices are always returned in CHOLMOD_COMPLEX format, // not CHOLMOD_ZOMPLEX. // // ----------------------------------------------------------------------------- // Triplet matrices: // ----------------------------------------------------------------------------- // // The first data line of a triplet matrix contains 3 or 4 integers: // // nrow ncol nnz stype // // where stype is optional (stype does not appear in the Matrix Market format). // The matrix is nrow-by-ncol. The following nnz lines (excluding comments // and blank lines) each contain a single entry. Duplicates are permitted, // and are summed in the output matrix. // // The stype is first derived from the Matrix Market header. If the stype // appears as the fourth integer in the first data line, it is determined from // that line. // // If stype is present, it denotes the storage format for the matrix. // stype = 0 denotes an unsymmetric matrix (same as Matrix Market "general"). // stype = -1 denotes a real symmetric or complex Hermitian matrix whose lower // triangular entries are stored. Entries may be present in the upper // triangular part, but these are ignored (same as Matrix Market // "real symmetric" and "complex Hermitian"). // stype = 1 denotes a real symmetric or complex Hermitian matrix whose upper // triangular entries are stored. Entries may be present in the lower // triangular part, but these are ignored. This option is not present // in the Matrix Market format. // // If stype is not present (no Matrix Market header and not in the first data // line) it is inferred from the rest of the data. If the matrix is // rectangular, or has entries in both the upper and lower triangular parts, // then it is assumed to be unsymmetric (stype=0). If only entries in the // lower triangular part are present, the matrix is assumed to have stype = -1. // If only entries in the upper triangular part are present, the matrix is // assumed to have stype = 1. // // After the first data line (with nrow, ncol, nnz, and optionally stype), // each nonzero consists of one line with 2, 3, or 4 entries. All lines must // have the same number of entries. The first two entries are the row and // column indices of the nonzero. If 3 entries are present, the 3rd entry is // the numerical value, and the matrix is real. If 4 entries are present, // the 3rd and 4th entries in the line are the real and imaginary parts of // a complex value. // // The matrix can be either 0-based or 1-based. It is first assumed to be // one-based (all matrices in the Matrix Market are one-based), with row indices // in the range 1 to ncol and column indices in the range 1 to nrow. If a row // or column index of zero is found, the matrix is assumed to be zero-based // (with row indices in the range 0 to ncol-1 and column indices in the range 0 // to nrow-1). // // If Common->prefer_binary is set to its default value of FALSE, then // for symmetric pattern-only matrices, the kth diagonal (if present) is set to // one plus the degree of the row/column k, and the off-diagonal entries are set // to -1. A symmetric pattern-only matrix with a zero-free diagonal is thus // converted into a symmetric positive definite matrix. All entries are set to // one for an unsymmetric pattern-only matrix. This differs from the // Matrix Market format (A = mmread ('file') returns a binary pattern for A for // symmetric pattern-only matrices). If Common->prefer_binary is TRUE, then // this function returns a binary matrix (just like mmread('file')). // // ----------------------------------------------------------------------------- // Dense matrices: // ----------------------------------------------------------------------------- // // A dense matrix is specified by the Matrix Market "array" format. The // Matrix Market header is optional; if not present, the matrix is assumed to // be in the Matrix Market "general" format. The first data line contains just // two integers: // // nrow ncol // // The can be real, integer, or complex (not pattern). These functions // convert an integer type to real. The entries in the matrix are stored in // column-major format, with one line per entry. Two entries are present in // each line for complex matrices, one for real and integer matrices. In // rectangular and unsymmetric matrices, all entries are present. For real // symmetric or complex Hermitian matrices, only entries in the lower triangular // part appear. For skew-symmetric matrices, only entries in the strictly // lower triangular part appear. // // Since CHOLMOD does not have a data structure for presenting dense symmetric/ // Hermitian matrices, these functions always return a dense matrix in its // general form, with both upper and lower parts present. #ifndef NCHECK #include "cholmod_internal.h" // The MatrixMarket format specificies a maximum line length of 1024 #define MAXLINE 1030 //------------------------------------------------------------------------------ // get_line //------------------------------------------------------------------------------ // Read one line of the file, return TRUE if successful, FALSE if EOF. static int get_line (FILE *f, char *buf) { buf [0] = '\0' ; buf [1] = '\0' ; buf [MAXLINE] = '\0' ; return (fgets (buf, MAXLINE, f) != NULL) ; } //------------------------------------------------------------------------------ // fix_inf //------------------------------------------------------------------------------ // Replace huge values with +/- Inf's, since scanf and printf don't deal // with Inf's properly. static double fix_inf (double x) { if (x >= HUGE_DOUBLE) { x = INFINITY ; } else if (x <= -HUGE_DOUBLE) { x = -INFINITY ; } return (x) ; } //------------------------------------------------------------------------------ // is_blank_line //------------------------------------------------------------------------------ // TRUE if s is a blank line or comment, FALSE otherwise static int is_blank_line ( char *s ) { int c, k ; if (s [0] == '%') { // a comment line return (TRUE) ; } for (k = 0 ; k <= MAXLINE ; k++) { c = s [k] ; if (c == '\0') { // end of line break ; } if (!isspace (c)) { // non-space character return (FALSE) ; } } return (TRUE) ; } //------------------------------------------------------------------------------ // read_header //------------------------------------------------------------------------------ // Read the header. This consists of zero or more comment lines (blank, or // starting with a "%" in the first column), followed by a single data line // containing up to four numerical values. // // The first line may optionally be a Matrix Market header line, of the form // // %%MatrixMarket matrix // // The first data line of a sparse matrix in triplet form consists of 3 or 4 // numerical values: // // nrow ncol nnz stype // // where stype is optional (it does not appear in the Matrix Market file // format). The first line of a dense matrix in column-major form consists of // two numerical values: // // nrow ncol // // The stype of the matrix is determine either from the Matrix Market header, // or (optionally) from the first data line. stypes of 0 to -3 directly // correlate with the Matrix Market format; stype = 1 is an extension to that // format. // // 999: unknown (will be inferred from the data) // 1: real symmetric or complex Hermitian with upper part stored // (not in the Matrix Market format) // 0: unsymmetric (same as Matrix Market "general") // -1: real symmetric or complex Hermitian, with lower part stored // (Matrix Market "real symmetric" or "complex hermitian") // -2: real or complex skew symmetric (lower part stored, can only be // specified by Matrix Market header) // -3: complex symmetric (lower part stored) // specified by Matrix Market header) // // The Matrix Market header is optional. If stype appears in the first data // line, it is determine by that data line. Otherwise, if the Matrix Market // header appears, stype is determined from that header. If stype does not // appear, it is set to "unknown" (999). #define STYPE_UNKNOWN 999 #define STYPE_SYMMETRIC_UPPER 1 #define STYPE_UNSYMMETRIC 0 #define STYPE_SYMMETRIC_LOWER -1 #define STYPE_SKEW_SYMMETRIC -2 #define STYPE_COMPLEX_SYMMETRIC_LOWER -3 static int read_header // returns TRUE if successful, FALSE on error ( // input: FILE *f, // file to read from // output: char *buf, // a character array of size MAXLINE+1 int *mtype, // CHOLMOD_TRIPLET or CHOLMOD_DENSE size_t *nrow, // number of rows in the matrix size_t *ncol, // number of columns in the matrix size_t *nnz, // number of entries in a triplet matrix (0 for dense) int *stype // stype (see above) ) { char *p ; int first = TRUE, got_mm_header = FALSE, c, c2, is_complex, nitems ; double l1, l2, l3, l4 ; *mtype = CHOLMOD_TRIPLET ; *nrow = 0 ; *ncol = 0 ; *nnz = 0 ; *stype = STYPE_UNKNOWN ; for ( ; ; ) { //---------------------------------------------------------------------- // get the next line //---------------------------------------------------------------------- if (!get_line (f, buf)) { // premature end of file return (FALSE) ; } if (first && (strncmp (buf, "%%MatrixMarket", 14) == 0)) { //------------------------------------------------------------------ // read a Matrix Market header //------------------------------------------------------------------ got_mm_header = TRUE ; p = buf ; //------------------------------------------------------------------ // get "matrix" token //------------------------------------------------------------------ while (*p && !isspace (*p)) p++ ; while (*p && isspace (*p)) p++ ; c = tolower (*p) ; if (c != 'm') { // bad format return (FALSE) ; } //------------------------------------------------------------------ // get the fmt token ("coord" or "array") //------------------------------------------------------------------ while (*p && !isspace (*p)) p++ ; while (*p && isspace (*p)) p++ ; c = tolower (*p) ; if (c == 'c') { *mtype = CHOLMOD_TRIPLET ; } else if (c == 'a') { *mtype = CHOLMOD_DENSE ; } else { // bad format, neither "coordinate" nor "array" return (FALSE) ; } //------------------------------------------------------------------ // get type token (real, pattern, complex, integer) //------------------------------------------------------------------ while (*p && !isspace (*p)) p++ ; while (*p && isspace (*p)) p++ ; c = tolower (*p) ; if (!(c == 'r' || c == 'p' || c == 'c' || c == 'i')) { // bad format return (FALSE) ; } is_complex = (c == 'c') ; //------------------------------------------------------------------ // get storage token (general, hermitian, symmetric, skew) //------------------------------------------------------------------ while (*p && !isspace (*p)) p++ ; while (*p && isspace (*p)) p++ ; c = tolower (*p) ; c2 = tolower (*(p+1)) ; if (c == 'g') { // "general" storage (unsymmetric matrix), both parts present *stype = STYPE_UNSYMMETRIC ; } else if (c == 's' && c2 == 'y') { // "symmetric" if (is_complex) { // complex symmetric, lower triangular part present *stype = STYPE_COMPLEX_SYMMETRIC_LOWER ; } else { // real symmetric, lower triangular part present *stype = STYPE_SYMMETRIC_LOWER ; } } else if (c == 'h') { // "hermitian" matrix, lower triangular part present *stype = STYPE_SYMMETRIC_LOWER ; } else if (c == 's' && c2 == 'k') { // "skew-symmetric" (real or complex), lower part present *stype = STYPE_SKEW_SYMMETRIC ; } else { // bad format return (FALSE) ; } } else if (is_blank_line (buf)) { //------------------------------------------------------------------ // blank line or comment line //------------------------------------------------------------------ continue ; } else { //------------------------------------------------------------------ // read the first data line and return //------------------------------------------------------------------ // format: nrow ncol nnz stype l1 = EMPTY ; l2 = EMPTY ; l3 = 0 ; l4 = 0 ; nitems = sscanf (buf, "%lg %lg %lg %lg\n", &l1, &l2, &l3, &l4) ; if (nitems < 2 || nitems > 4 || l1 > (double) Int_max || l2 > (double) Int_max) { // invalid matrix return (FALSE) ; } *nrow = l1 ; *ncol = l2 ; if (nitems == 2) { // a dense matrix if (!got_mm_header) { *mtype = CHOLMOD_DENSE ; *stype = STYPE_UNSYMMETRIC ; } } if (nitems == 3 || nitems == 4) { // a sparse triplet matrix *nnz = l3 ; if (!got_mm_header) { *mtype = CHOLMOD_TRIPLET ; } } if (nitems == 4) { // an stype specified here can only be 1, 0, or -1 if (l4 < 0) { *stype = STYPE_SYMMETRIC_LOWER ; } else if (l4 > 0) { *stype = STYPE_SYMMETRIC_UPPER ; } else { *stype = STYPE_UNSYMMETRIC ; } } if (*nrow != *ncol) { // a rectangular matrix must be unsymmetric *stype = STYPE_UNSYMMETRIC ; } return (TRUE) ; } first = FALSE ; } } //------------------------------------------------------------------------------ // read_triplet //------------------------------------------------------------------------------ // Header has already been read in, including first line (nrow ncol nnz stype). // Read the triplets. static cholmod_triplet *read_triplet ( // input: FILE *f, // file to read from, must already be open size_t nrow, // number of rows size_t ncol, // number of columns size_t nnz, // number of triplets in file to read int stype, // stype from header, or "unknown" int prefer_unsym, // if TRUE, always return T->stype of zero int dtype, // CHOLMOD_DOUBLE or CHOLMOD_SINGLE // workspace: char *buf, // of size MAXLINE+1 cholmod_common *Common ) { double x, z ; Int *Ti, *Tj, *Rdeg, *Cdeg ; cholmod_triplet *T ; double l1, l2 ; Int nitems, xtype, unknown, k, nshould, is_lower, is_upper, one_based, i, j, imax, jmax, skew_symmetric, p, complex_symmetric ; //-------------------------------------------------------------------------- // quick return for empty matrix //-------------------------------------------------------------------------- if (nrow == 0 || ncol == 0 || nnz == 0) { // return an empty matrix return (CHOLMOD(allocate_triplet) (nrow, ncol, 0, 0, CHOLMOD_REAL + dtype, Common)) ; } //-------------------------------------------------------------------------- // special stype cases: unknown, skew symmetric, and complex symmetric //-------------------------------------------------------------------------- unknown = (stype == STYPE_UNKNOWN) ; skew_symmetric = (stype == STYPE_SKEW_SYMMETRIC) ; complex_symmetric = (stype == STYPE_COMPLEX_SYMMETRIC_LOWER) ; size_t extra = 0 ; if (stype < STYPE_SYMMETRIC_LOWER || (prefer_unsym && stype != STYPE_UNSYMMETRIC)) { // 999: unknown might be converted to unsymmetric // 1: symmetric upper converted to unsym. if prefer_unsym is TRUE // -1: symmetric lower converted to unsym. if prefer_unsym is TRUE // -2: real or complex skew symmetric converted to unsymmetric // -3: complex symmetric converted to unsymmetric stype = STYPE_UNSYMMETRIC ; extra = nnz ; } int ok = TRUE ; size_t nnz2 = CHOLMOD(add_size_t) (nnz, extra, &ok) ; //-------------------------------------------------------------------------- // allocate workspace //-------------------------------------------------------------------------- // s = nrow + ncol size_t s = CHOLMOD(add_size_t) (nrow, ncol, &ok) ; if (!ok || nrow > Int_max || ncol > Int_max || nnz > Int_max) { ERROR (CHOLMOD_TOO_LARGE, "problem too large") ; return (NULL) ; } CHOLMOD(allocate_work) (0, s, 0, Common) ; Rdeg = Common->Iwork ; // size nrow Cdeg = Rdeg + nrow ; // size ncol //-------------------------------------------------------------------------- // read the triplets //-------------------------------------------------------------------------- is_lower = TRUE ; is_upper = TRUE ; one_based = TRUE ; imax = 0 ; jmax = 0 ; Ti = NULL ; Tj = NULL ; xtype = 999 ; nshould = 0 ; for (k = 0 ; k < (Int) nnz ; k++) { //---------------------------------------------------------------------- // get the next triplet, skipping blank lines and comment lines //---------------------------------------------------------------------- l1 = EMPTY ; l2 = EMPTY ; x = 0 ; z = 0 ; for ( ; ; ) { if (!get_line (f, buf)) { // premature end of file - not enough triplets read in ERROR (CHOLMOD_INVALID, "premature EOF") ; return (NULL) ; } if (is_blank_line (buf)) { // blank line or comment continue ; } nitems = sscanf (buf, "%lg %lg %lg %lg\n", &l1, &l2, &x, &z) ; x = fix_inf (x) ; z = fix_inf (z) ; break ; } nitems = (nitems == EOF) ? 0 : nitems ; i = l1 ; j = l2 ; //---------------------------------------------------------------------- // for first triplet: determine type and allocate triplet matrix //---------------------------------------------------------------------- if (k == 0) { if (nitems < 2 || nitems > 4) { // invalid matrix ERROR (CHOLMOD_INVALID, "invalid format") ; return (NULL) ; } else if (nitems == 2) { // this will be converted into a real matrix later xtype = CHOLMOD_PATTERN ; } else if (nitems == 3) { xtype = CHOLMOD_REAL ; } else if (nitems == 4) { xtype = CHOLMOD_COMPLEX ; } // the rest of the lines should have the same number of entries nshould = nitems ; // allocate triplet matrix T = CHOLMOD(allocate_triplet) (nrow, ncol, nnz2, stype, (xtype == CHOLMOD_PATTERN ? CHOLMOD_REAL : xtype) + dtype, Common) ; if (Common->status < CHOLMOD_OK) { // out of memory return (NULL) ; } Ti = T->i ; Tj = T->j ; T->nnz = nnz ; } //---------------------------------------------------------------------- // save the entry in the triplet matrix //---------------------------------------------------------------------- if (nitems != nshould || i < 0 || j < 0) { // wrong format, premature end-of-file, or negative indices CHOLMOD(free_triplet) (&T, Common) ; ERROR (CHOLMOD_INVALID, "invalid matrix file") ; return (NULL) ; } Ti [k] = i ; Tj [k] = j ; if (i < j) { // this entry is in the upper triangular part is_lower = FALSE ; } if (i > j) { // this entry is in the lower triangular part is_upper = FALSE ; } #define ASSIGN_TRIPLET_VALUE(fltype) \ { \ fltype *Tx = (fltype *) T->x ; \ if (xtype == CHOLMOD_REAL) \ { \ Tx [k] = x ; \ } \ else if (xtype == CHOLMOD_COMPLEX) \ { \ Tx [2*k ] = x ; \ Tx [2*k+1] = z ; \ } \ } if (dtype == CHOLMOD_DOUBLE) { ASSIGN_TRIPLET_VALUE (double) ; } else { ASSIGN_TRIPLET_VALUE (float) ; } if (i == 0 || j == 0) { one_based = FALSE ; } imax = MAX (i, imax) ; jmax = MAX (j, jmax) ; } //-------------------------------------------------------------------------- // convert to zero-based //-------------------------------------------------------------------------- if (one_based) { // input matrix is one-based; convert matrix to zero-based for (k = 0 ; k < (Int) nnz ; k++) { Ti [k]-- ; Tj [k]-- ; } } if (one_based ? (imax > (Int) nrow || jmax > (Int) ncol) : (imax >= (Int) nrow || jmax >= (Int) ncol)) { // indices out of range CHOLMOD(free_triplet) (&T, Common) ; ERROR (CHOLMOD_INVALID, "indices out of range") ; return (NULL) ; } //-------------------------------------------------------------------------- // determine the stype, if not yet known //-------------------------------------------------------------------------- if (unknown) { if (is_lower && is_upper) { // diagonal matrix, symmetric with upper part present stype = STYPE_SYMMETRIC_UPPER ; } else if (is_lower && !is_upper) { // symmetric, lower triangular part present stype = STYPE_SYMMETRIC_LOWER ; } else if (!is_lower && is_upper) { // symmetric, upper triangular part present stype = STYPE_SYMMETRIC_UPPER ; } else { // unsymmetric stype = STYPE_UNSYMMETRIC ; extra = 0 ; } } //-------------------------------------------------------------------------- // add the remainder of symmetric, skew-symmetric or Hermitian matrices //-------------------------------------------------------------------------- // note that this step is not done for real symmetric or complex Hermitian // matrices, unless prefer_unsym is TRUE if (extra > 0) { p = nnz ; for (k = 0 ; k < (Int) nnz ; k++) { i = Ti [k] ; j = Tj [k] ; if (i != j) { Ti [p] = j ; Tj [p] = i ; #define ASSIGN_SYMMETRIC_TRIPLET(fltype) \ { \ fltype *Tx = (fltype *) T->x ; \ if (xtype == CHOLMOD_REAL) \ { \ if (skew_symmetric) \ { \ Tx [p] = -Tx [k] ; \ } \ else \ { \ Tx [p] = Tx [k] ; \ } \ } \ else if (xtype == CHOLMOD_COMPLEX) \ { \ if (skew_symmetric) \ { \ Tx [2*p ] = -Tx [2*k ] ; \ Tx [2*p+1] = -Tx [2*k+1] ; \ } \ else if (complex_symmetric) \ { \ Tx [2*p ] = Tx [2*k ] ; \ Tx [2*p+1] = Tx [2*k+1] ; \ } \ else /* Hermitian */ \ { \ Tx [2*p ] = Tx [2*k ] ; \ Tx [2*p+1] = -Tx [2*k+1] ; \ } \ } \ } if (dtype == CHOLMOD_DOUBLE) { ASSIGN_SYMMETRIC_TRIPLET (double) ; } else { ASSIGN_SYMMETRIC_TRIPLET (float) ; } p++ ; } } T->nnz = p ; nnz = p ; } T->stype = stype ; //-------------------------------------------------------------------------- // create values for a pattern-only matrix //-------------------------------------------------------------------------- if (xtype == CHOLMOD_PATTERN) { if (stype == STYPE_UNSYMMETRIC || Common->prefer_binary) { // unsymmetric case, or binary case #define TRIPLETS_ALL_ONE(fltype) \ { \ fltype *Tx = (fltype *) T->x ; \ for (k = 0 ; k < (Int) nnz ; k++) \ { \ Tx [k] = 1 ; \ } \ } if (dtype == CHOLMOD_DOUBLE) { TRIPLETS_ALL_ONE (double) ; } else { TRIPLETS_ALL_ONE (float) ; } } else { // compute the row and columm degrees (excluding the diagonal) for (i = 0 ; i < (Int) nrow ; i++) { Rdeg [i] = 0 ; } for (j = 0 ; j < (Int) ncol ; j++) { Cdeg [j] = 0 ; } for (k = 0 ; k < (Int) nnz ; k++) { i = Ti [k] ; j = Tj [k] ; if ((stype < 0 && i > j) || (stype > 0 && i < j)) { // both a(i,j) and a(j,i) appear in the matrix Rdeg [i]++ ; Cdeg [j]++ ; Rdeg [j]++ ; Cdeg [i]++ ; } } // assign the numerical values #define TRIPLET_LAPLACIAN(fltype) \ { \ fltype *Tx = (fltype *) T->x ; \ for (k = 0 ; k < (Int) nnz ; k++) \ { \ i = Ti [k] ; \ j = Tj [k] ; \ Tx [k] = (i == j) ? (1 + MAX (Rdeg[i], Cdeg[j])) : (-1) ; \ } \ } if (dtype == CHOLMOD_DOUBLE) { TRIPLET_LAPLACIAN (double) ; } else { TRIPLET_LAPLACIAN (float) ; } } } //-------------------------------------------------------------------------- // return the new triplet matrix //-------------------------------------------------------------------------- return (T) ; } //------------------------------------------------------------------------------ // read_dense //------------------------------------------------------------------------------ // Header has already been read in, including first line (nrow ncol). // Read a dense matrix. static cholmod_dense *read_dense ( // input: FILE *f, // file to read from, must already be open size_t nrow, // number of rows size_t ncol, // number of columns int stype, // stype from header int dtype, // CHOLMOD_DOUBLE or CHOLMOD_SINGLE // workspace: char *buf, // of size MAXLINE+1 cholmod_common *Common ) { double x, z ; cholmod_dense *X ; Int nitems, xtype = -1, nshould = 0, i, j, k, kup, first ; //-------------------------------------------------------------------------- // quick return for empty matrix //-------------------------------------------------------------------------- if (nrow == 0 || ncol == 0) { // return an empty dense matrix return (CHOLMOD(zeros) (nrow, ncol, CHOLMOD_REAL + dtype, Common)) ; } //-------------------------------------------------------------------------- // read the entries //-------------------------------------------------------------------------- first = TRUE ; for (j = 0 ; j < (Int) ncol ; j++) { //---------------------------------------------------------------------- // get the row index of the first entry in the file for column j //---------------------------------------------------------------------- if (stype == STYPE_UNSYMMETRIC) { i = 0 ; } else if (stype == STYPE_SKEW_SYMMETRIC) { i = j+1 ; } else // real symmetric or complex Hermitian lower { i = j ; } //---------------------------------------------------------------------- // get column j //---------------------------------------------------------------------- for ( ; i < (Int) nrow ; i++) { //------------------------------------------------------------------ // get the next entry, skipping blank lines and comment lines //------------------------------------------------------------------ x = 0 ; z = 0 ; for ( ; ; ) { if (!get_line (f, buf)) { // premature end of file - not enough entries read in ERROR (CHOLMOD_INVALID, "premature EOF") ; return (NULL) ; } if (is_blank_line (buf)) { // blank line or comment continue ; } nitems = sscanf (buf, "%lg %lg\n", &x, &z) ; x = fix_inf (x) ; z = fix_inf (z) ; break ; } nitems = (nitems == EOF) ? 0 : nitems ; //------------------------------------------------------------------ // for first entry: determine type and allocate dense matrix //------------------------------------------------------------------ if (first) { first = FALSE ; if (nitems < 1 || nitems > 2) { // invalid matrix ERROR (CHOLMOD_INVALID, "invalid format") ; return (NULL) ; } else if (nitems == 1) { // a real matrix xtype = CHOLMOD_REAL ; } else if (nitems == 2) { // a complex matrix xtype = CHOLMOD_COMPLEX ; } // the rest of the lines should have same number of entries nshould = nitems ; // allocate the result X = CHOLMOD(zeros) (nrow, ncol, xtype + dtype, Common) ; if (Common->status < CHOLMOD_OK) { // out of memory return (NULL) ; } } //------------------------------------------------------------------ // save the entry in the dense matrix //------------------------------------------------------------------ if (nitems != nshould) { // wrong format or premature end-of-file CHOLMOD(free_dense) (&X, Common) ; ERROR (CHOLMOD_INVALID, "invalid matrix file") ; return (NULL) ; } k = i + j*nrow ; kup = j + i*nrow ; #define ASSIGN_DENSE_VALUE(fltype) \ { \ fltype *Xx = (fltype *) X->x ; \ if (xtype == CHOLMOD_REAL) \ { \ /* real matrix */ \ Xx [k] = x ; \ if (k != kup) \ { \ if (stype == STYPE_SYMMETRIC_LOWER) \ { \ /* real symmetric matrix */ \ Xx [kup] = x ; \ } \ else if (stype == STYPE_SKEW_SYMMETRIC) \ { \ /* real skew symmetric matrix */ \ Xx [kup] = -x ; \ } \ } \ } \ else if (xtype == CHOLMOD_COMPLEX) \ { \ Xx [2*k ] = x ; /* real part */ \ Xx [2*k+1] = z ; /* imaginary part */ \ if (k != kup) \ { \ if (stype == STYPE_SYMMETRIC_LOWER) \ { \ /* complex Hermitian */ \ Xx [2*kup ] = x ; /* real part */ \ Xx [2*kup+1] = -z ; /* imaginary part */ \ } \ else if (stype == STYPE_SKEW_SYMMETRIC) \ { \ /* complex skew symmetric */ \ Xx [2*kup ] = -x ; /* real part */ \ Xx [2*kup+1] = -z ; /* imaginary part */ \ } \ if (stype == STYPE_COMPLEX_SYMMETRIC_LOWER) \ { \ /* complex symmetric */ \ Xx [2*kup ] = x ; /* real part */ \ Xx [2*kup+1] = z ; /* imaginary part */ \ } \ } \ } \ } if (dtype == CHOLMOD_DOUBLE) { ASSIGN_DENSE_VALUE (double) ; } else { ASSIGN_DENSE_VALUE (float) ; } } } //-------------------------------------------------------------------------- // return the new dense matrix //-------------------------------------------------------------------------- return (X) ; } //------------------------------------------------------------------------------ // cholmod_read_triplet //------------------------------------------------------------------------------ // Read in a triplet matrix from a file (as double, not float) cholmod_triplet *CHOLMOD(read_triplet) ( // input: FILE *f, // file to read from, must already be open cholmod_common *Common ) { return (CHOLMOD(read_triplet2) (f, CHOLMOD_DOUBLE, Common)) ; } //------------------------------------------------------------------------------ // cholmod_read_triplet2 //------------------------------------------------------------------------------ // Read in a triplet matrix from a file (as float or double) cholmod_triplet *CHOLMOD(read_triplet2) ( // input: FILE *f, // file to read from, must already be open int dtype, // CHOLMOD_DOUBLE or CHOLMOD_SINGLE cholmod_common *Common ) { char buf [MAXLINE+1] ; size_t nrow, ncol, nnz ; int stype, mtype ; //-------------------------------------------------------------------------- // check inputs //-------------------------------------------------------------------------- RETURN_IF_NULL_COMMON (NULL) ; RETURN_IF_NULL (f, NULL) ; Common->status = CHOLMOD_OK ; //-------------------------------------------------------------------------- // read the header and first data line //-------------------------------------------------------------------------- if (!read_header (f, buf, &mtype, &nrow, &ncol, &nnz, &stype) || mtype != CHOLMOD_TRIPLET) { // invalid matrix - this function can only read in a triplet matrix ERROR (CHOLMOD_INVALID, "invalid format") ; return (NULL) ; } //-------------------------------------------------------------------------- // read the triplet matrix //-------------------------------------------------------------------------- return (read_triplet (f, nrow, ncol, nnz, stype, FALSE, dtype, buf, Common)) ; } //------------------------------------------------------------------------------ // cholmod_read_sparse //------------------------------------------------------------------------------ // Read a sparse matrix from a file (as double). See cholmod_read_triplet for // a discussion of the file format. // // If Common->prefer_upper is TRUE (the default case), a symmetric matrix is // returned stored in upper-triangular form (A->stype == 1). cholmod_sparse *CHOLMOD(read_sparse) ( // input: FILE *f, // file to read from, must already be open cholmod_common *Common ) { return (CHOLMOD(read_sparse2) (f, CHOLMOD_DOUBLE, Common)) ; } //------------------------------------------------------------------------------ // cholmod_read_sparse2: read sparse matrix from a file (double or single) //------------------------------------------------------------------------------ cholmod_sparse *CHOLMOD(read_sparse2) ( // input: FILE *f, // file to read from, must already be open int dtype, // CHOLMOD_DOUBLE or CHOLMOD_SINGLE cholmod_common *Common ) { cholmod_sparse *A, *A2 ; cholmod_triplet *T ; //-------------------------------------------------------------------------- // check inputs //-------------------------------------------------------------------------- RETURN_IF_NULL_COMMON (NULL) ; RETURN_IF_NULL (f, NULL) ; Common->status = CHOLMOD_OK ; //-------------------------------------------------------------------------- // convert to a sparse matrix in compressed-column form //-------------------------------------------------------------------------- T = CHOLMOD(read_triplet2) (f, dtype, Common) ; A = CHOLMOD(triplet_to_sparse) (T, 0, Common) ; CHOLMOD(free_triplet) (&T, Common) ; if (Common->prefer_upper && A != NULL && A->stype == -1) { // A=A' A2 = CHOLMOD(transpose) (A, 2, Common) ; CHOLMOD(free_sparse) (&A, Common) ; A = A2 ; } return (A) ; } //------------------------------------------------------------------------------ // cholmod_read_dense //------------------------------------------------------------------------------ // Read a dense matrix from a file (double only). cholmod_dense *CHOLMOD(read_dense) ( // input: FILE *f, // file to read from, must already be open cholmod_common *Common ) { return (CHOLMOD(read_dense2) (f, CHOLMOD_DOUBLE, Common)) ; } //------------------------------------------------------------------------------ // cholmod_read_dense2 //------------------------------------------------------------------------------ // Read a dense matrix from a file (float or double). cholmod_dense *CHOLMOD(read_dense2) ( // input: FILE *f, // file to read from, must already be open int dtype, // CHOLMOD_DOUBLE or CHOLMOD_SINGLE cholmod_common *Common ) { char buf [MAXLINE+1] ; size_t nrow, ncol, nnz ; int stype, mtype ; //-------------------------------------------------------------------------- // check inputs //-------------------------------------------------------------------------- RETURN_IF_NULL_COMMON (NULL) ; RETURN_IF_NULL (f, NULL) ; Common->status = CHOLMOD_OK ; //-------------------------------------------------------------------------- // read the header and first data line //-------------------------------------------------------------------------- if (!read_header (f, buf, &mtype, &nrow, &ncol, &nnz, &stype) || mtype != CHOLMOD_DENSE) { // invalid matrix - this function can only read in a dense matrix ERROR (CHOLMOD_INVALID, "invalid format") ; return (NULL) ; } //-------------------------------------------------------------------------- // read the dense matrix //-------------------------------------------------------------------------- return (read_dense (f, nrow, ncol, stype, dtype, buf, Common)) ; } //------------------------------------------------------------------------------ // cholmod_read_matrix //------------------------------------------------------------------------------ // Read a triplet matrix, sparse matrix or a dense matrix from a file. Returns // a void pointer to either a cholmod_triplet, cholmod_sparse, or cholmod_dense // object. The type of object is passed back to the caller as the mtype // argument. The matrix is read in double precision only. void *CHOLMOD(read_matrix) ( // input: FILE *f, // file to read from, must already be open int prefer, // If 0, a sparse matrix is always return as a // cholmod_triplet form. It can have any stype // (symmetric-lower, unsymmetric, or symmetric-upper). // If 1, a sparse matrix is returned as an unsymmetric // cholmod_sparse form (A->stype == 0), with both upper and // lower triangular parts present. This is what the MATLAB // mread mexFunction does, since MATLAB does not have an // stype. // If 2, a sparse matrix is returned with an stype of 0 or // 1 (unsymmetric, or symmetric with upper part stored). // This argument has no effect for dense matrices. // output: int *mtype, // CHOLMOD_TRIPLET, CHOLMOD_SPARSE or CHOLMOD_DENSE cholmod_common *Common ) { return (CHOLMOD(read_matrix2) (f, prefer, CHOLMOD_DOUBLE, mtype, Common)) ; } //------------------------------------------------------------------------------ // cholmod_read_matrix2: same as cholmod_read_matrix, but float or double //------------------------------------------------------------------------------ void *CHOLMOD(read_matrix2) ( // input: FILE *f, // file to read from, must already be open int prefer, // see cholmod_read_matrix int dtype, // CHOLMOD_DOUBLE or CHOLMOD_SINGLE // output: int *mtype, // CHOLMOD_TRIPLET, CHOLMOD_SPARSE or CHOLMOD_DENSE cholmod_common *Common ) { void *G = NULL ; cholmod_sparse *A, *A2 ; cholmod_triplet *T ; char buf [MAXLINE+1] ; size_t nrow, ncol, nnz ; int stype ; //-------------------------------------------------------------------------- // check inputs //-------------------------------------------------------------------------- RETURN_IF_NULL_COMMON (NULL) ; RETURN_IF_NULL (f, NULL) ; RETURN_IF_NULL (mtype, NULL) ; Common->status = CHOLMOD_OK ; //-------------------------------------------------------------------------- // read the header to determine the mtype //-------------------------------------------------------------------------- if (!read_header (f, buf, mtype, &nrow, &ncol, &nnz, &stype)) { // invalid matrix ERROR (CHOLMOD_INVALID, "invalid format") ; return (NULL) ; } //-------------------------------------------------------------------------- // read a matrix //-------------------------------------------------------------------------- if (*mtype == CHOLMOD_TRIPLET) { // read in the triplet matrix, converting to unsymmetric format if // prefer == 1 T = read_triplet (f, nrow, ncol, nnz, stype, prefer == 1, dtype, buf, Common) ; if (prefer == 0) { // return matrix in its original triplet form G = T ; } else { // return matrix in a compressed-column form A = CHOLMOD(triplet_to_sparse) (T, 0, Common) ; CHOLMOD(free_triplet) (&T, Common) ; if (A != NULL && prefer == 2 && A->stype == -1) { // convert A from symmetric-lower to symmetric-upper A2 = CHOLMOD(transpose) (A, 2, Common) ; CHOLMOD(free_sparse) (&A, Common) ; A = A2 ; } *mtype = CHOLMOD_SPARSE ; G = A ; } } else if (*mtype == CHOLMOD_DENSE) { // return a dense matrix G = read_dense (f, nrow, ncol, stype, dtype, buf, Common) ; } return (G) ; } #endif