svdlib.c

Go to the documentation of this file.
00001 #include <stdio.h>
00002 #include <stdlib.h>
00003 #include "svdlib.h"
00004 #include "svdutil.h"
00005 
00006 char *SVDVersion = "1.34";
00007 long SVDVerbosity = 1;
00008 long SVDCount[SVD_COUNTERS];
00009 
00010 void svdResetCounters(void) {
00011   int i;
00012   for (i = 0; i < SVD_COUNTERS; i++)
00013     SVDCount[i] = 0;
00014 }
00015 
00016 /********************************* Allocation ********************************/
00017 
00018 /* Row major order.  Rows are vectors that are consecutive in memory.  Matrix
00019    is initialized to empty. */
00020 DMat svdNewDMat(int rows, int cols) {
00021   int i;
00022   DMat D = (DMat) malloc(sizeof(struct dmat));
00023   if (!D) {perror("svdNewDMat"); return NULL;}
00024   D->rows = rows;
00025   D->cols = cols;
00026 
00027   D->value = (double **) malloc(rows * sizeof(double *));
00028   if (!D->value) {SAFE_FREE(D); return NULL;}
00029 
00030   D->value[0] = (double *) calloc(rows * cols, sizeof(double));
00031   if (!D->value[0]) {SAFE_FREE(D->value); SAFE_FREE(D); return NULL;}
00032 
00033   for (i = 1; i < rows; i++) D->value[i] = D->value[i-1] + cols;
00034   return D;
00035 }
00036 
00037 void svdFreeDMat(DMat D) {
00038   if (!D) return;
00039   SAFE_FREE(D->value[0]);
00040   SAFE_FREE(D->value);
00041   free(D);
00042 }
00043 
00044 
00045 SMat svdNewSMat(int rows, int cols, int vals) {
00046   SMat S = (SMat) calloc(1, sizeof(struct smat));
00047   if (!S) {perror("svdNewSMat"); return NULL;}
00048   S->rows = rows;
00049   S->cols = cols;
00050   S->vals = vals;
00051   S->pointr = svd_longArray(cols + 1, TRUE, "svdNewSMat: pointr");
00052   if (!S->pointr) {svdFreeSMat(S); return NULL;}
00053   S->rowind = svd_longArray(vals, FALSE, "svdNewSMat: rowind");
00054   if (!S->rowind) {svdFreeSMat(S); return NULL;}
00055   S->value  = svd_doubleArray(vals, FALSE, "svdNewSMat: value");
00056   if (!S->value)  {svdFreeSMat(S); return NULL;}
00057   return S;
00058 }
00059 
00060 void svdFreeSMat(SMat S) {
00061   if (!S) return;
00062   SAFE_FREE(S->pointr);
00063   SAFE_FREE(S->rowind);
00064   SAFE_FREE(S->value);
00065   free(S);
00066 }
00067 
00068 
00069 /* Creates an empty SVD record */
00070 SVDRec svdNewSVDRec(void) {
00071   SVDRec R = (SVDRec) calloc(1, sizeof(struct svdrec));
00072   if (!R) {perror("svdNewSVDRec"); return NULL;}
00073   return R;
00074 }
00075 
00076 /* Frees an svd rec and all its contents. */
00077 void svdFreeSVDRec(SVDRec R) {
00078   if (!R) return;
00079   if (R->Ut) svdFreeDMat(R->Ut);
00080   if (R->S) SAFE_FREE(R->S);
00081   if (R->Vt) svdFreeDMat(R->Vt);
00082   free(R);
00083 }
00084 
00085 
00086 /**************************** Conversion *************************************/
00087 
00088 /* Converts a sparse matrix to a dense one (without affecting the former) */
00089 DMat svdConvertStoD(SMat S) {
00090   int i, c;
00091   DMat D = svdNewDMat(S->rows, S->cols);
00092   if (!D) {
00093     svd_error("svdConvertStoD: failed to allocate D");
00094     return NULL;
00095   }
00096   for (i = 0, c = 0; i < S->vals; i++) {
00097     while (S->pointr[c + 1] <= i) c++;
00098     D->value[S->rowind[i]][c] = S->value[i];
00099   }
00100   return D;
00101 }
00102 
00103 /* Converts a dense matrix to a sparse one (without affecting the dense one) */
00104 SMat svdConvertDtoS(DMat D) {
00105   SMat S;
00106   int i, j, n;
00107   for (i = 0, n = 0; i < D->rows; i++)
00108     for (j = 0; j < D->cols; j++)
00109       if (D->value[i][j] != 0) n++;
00110   
00111   S = svdNewSMat(D->rows, D->cols, n);
00112   if (!S) {
00113     svd_error("svdConvertDtoS: failed to allocate S");
00114     return NULL;
00115   }
00116   for (j = 0, n = 0; j < D->cols; j++) {
00117     S->pointr[j] = n;
00118     for (i = 0; i < D->rows; i++)
00119       if (D->value[i][j] != 0) {
00120         S->rowind[n] = i;
00121         S->value[n] = D->value[i][j];
00122         n++;
00123       }
00124   }
00125   S->pointr[S->cols] = S->vals;
00126   return S;
00127 }
00128 
00129 /* Transposes a dense matrix. */
00130 DMat svdTransposeD(DMat D) {
00131   int r, c;
00132   DMat N = svdNewDMat(D->cols, D->rows);
00133   for (r = 0; r < D->rows; r++)
00134     for (c = 0; c < D->cols; c++)
00135       N->value[c][r] = D->value[r][c];
00136   return N;
00137 }
00138 
00139 /* Efficiently transposes a sparse matrix. */
00140 SMat svdTransposeS(SMat S) {
00141   int r, c, i, j;
00142   SMat N = svdNewSMat(S->cols, S->rows, S->vals);
00143   /* Count number nz in each row. */
00144   for (i = 0; i < S->vals; i++)
00145     N->pointr[S->rowind[i]]++;
00146   /* Fill each cell with the starting point of the previous row. */
00147   N->pointr[S->rows] = S->vals - N->pointr[S->rows - 1];
00148   for (r = S->rows - 1; r > 0; r--)
00149     N->pointr[r] = N->pointr[r+1] - N->pointr[r-1];
00150   N->pointr[0] = 0;
00151   /* Assign the new columns and values. */
00152   for (c = 0, i = 0; c < S->cols; c++) {
00153     for (; i < S->pointr[c+1]; i++) {
00154       r = S->rowind[i];
00155       j = N->pointr[r+1]++;
00156       N->rowind[j] = c;
00157       N->value[j] = S->value[i];
00158     }
00159   }
00160   return N;
00161 }
00162 
00163 
00164 /**************************** Input/Output ***********************************/
00165 
00166 void svdWriteDenseArray(double *a, int n, char *filename, char binary) {
00167   int i;
00168   FILE *file = svd_writeFile(filename, FALSE);
00169   if (!file) 
00170     return svd_error("svdWriteDenseArray: failed to write %s", filename);
00171   if (binary) {
00172     svd_writeBinInt(file, n);
00173     for (i = 0; i < n; i++)
00174       svd_writeBinFloat(file, (float) a[i]);
00175   } else {
00176     fprintf(file, "%d\n", n);
00177     for (i = 0; i < n; i++)
00178       fprintf(file, "%g\n", a[i]);
00179   }
00180   svd_closeFile(file);
00181 }
00182 
00183 double *svdLoadDenseArray(char *filename, int *np, char binary) {
00184   int i, n;
00185   double *a;
00186 
00187   FILE *file = svd_readFile(filename);
00188   if (!file) {
00189     svd_error("svdLoadDenseArray: failed to read %s", filename);
00190     return NULL;
00191   }
00192   if (binary) svd_readBinInt(file, np);
00193   else fscanf(file, " %d", np);
00194   n = *np;
00195   a = svd_doubleArray(n, FALSE, "svdLoadDenseArray: a");
00196   if (!a) return NULL;
00197   if (binary) {
00198     float f;
00199     for (i = 0; i < n; i++) {
00200       svd_readBinFloat(file, &f);
00201       a[i] = f;
00202     }
00203   } else {
00204     for (i = 0; i < n; i++)
00205       fscanf(file, " %lf\n", a + i);
00206   }
00207   svd_closeFile(file);
00208   return a;
00209 }
00210 
00211 
00212 /* File format has a funny header, then first entry index per column, then the
00213    row for each entry, then the value for each entry.  Indices count from 1.
00214    Assumes A is initialized. */
00215 static SMat svdLoadSparseTextHBFile(FILE *file) {
00216   char line[128];
00217   long i, x, rows, cols, vals, num_mat;
00218   SMat S;
00219   /* Skip the header line: */
00220   if (!fgets(line, 128, file));
00221   /* Skip the line giving the number of lines in this file: */
00222   if (!fgets(line, 128, file));
00223   /* Read the line with useful dimensions: */
00224   if (fscanf(file, "%*s%ld%ld%ld%ld\n", 
00225              &rows, &cols, &vals, &num_mat) != 4) {
00226     svd_error("svdLoadSparseTextHBFile: bad file format on line 3");
00227     return NULL;
00228   }
00229   if (num_mat != 0) {
00230     svd_error("svdLoadSparseTextHBFile: I don't know how to handle a file "
00231               "with elemental matrices (last entry on header line 3)");
00232     return NULL;
00233   }
00234   /* Skip the line giving the formats: */
00235   if (!fgets(line, 128, file));
00236   
00237   S = svdNewSMat(rows, cols, vals);
00238   if (!S) return NULL;
00239   
00240   /* Read column pointers. */
00241   for (i = 0; i <= S->cols; i++) {
00242     if (fscanf(file, " %ld", &x) != 1) {
00243       svd_error("svdLoadSparseTextHBFile: error reading pointr %d", i);
00244       return NULL;
00245     }
00246     S->pointr[i] = x - 1;
00247   }
00248   S->pointr[S->cols] = S->vals;
00249   
00250   /* Read row indices. */
00251   for (i = 0; i < S->vals; i++) {
00252     if (fscanf(file, " %ld", &x) != 1) {
00253       svd_error("svdLoadSparseTextHBFile: error reading rowind %d", i);
00254       return NULL;
00255     }
00256     S->rowind[i] = x - 1;
00257   }
00258   for (i = 0; i < S->vals; i++) 
00259     if (fscanf(file, " %lf", S->value + i) != 1) {
00260       svd_error("svdLoadSparseTextHBFile: error reading value %d", i);
00261       return NULL;
00262     }
00263   return S;
00264 }
00265 
00266 static void svdWriteSparseTextHBFile(SMat S, FILE *file) {
00267   int i;
00268   long col_lines = ((S->cols + 1) / 8) + (((S->cols + 1) % 8) ? 1 : 0);
00269   long row_lines = (S->vals / 8) + ((S->vals % 8) ? 1 : 0);
00270   long total_lines = col_lines + 2 * row_lines;
00271   
00272   char title[32];
00273   sprintf(title, "SVDLIBC v. %s", SVDVersion);
00274   fprintf(file, "%-72s%-8s\n", title, "<key>");
00275   fprintf(file, "%14ld%14ld%14ld%14ld%14d\n", total_lines, col_lines,
00276           row_lines, row_lines, 0);
00277   fprintf(file, "%-14s%14ld%14ld%14ld%14d\n", "rra", S->rows, S->cols,
00278           S->vals, 0);
00279   fprintf(file, "%16s%16s%16s%16s\n", "(8i)", "(8i)", "(8e)", "(8e)");
00280 
00281   for (i = 0; i <= S->cols; i++)
00282     fprintf(file, "%ld%s", S->pointr[i] + 1, (((i+1) % 8) == 0) ? "\n" : " ");
00283   fprintf(file, "\n");
00284   for (i = 0; i < S->vals; i++)
00285     fprintf(file, "%ld%s", S->rowind[i] + 1, (((i+1) % 8) == 0) ? "\n" : " ");
00286   fprintf(file, "\n");
00287   for (i = 0; i < S->vals; i++)
00288     fprintf(file, "%g%s", S->value[i], (((i+1) % 8) == 0) ? "\n" : " ");
00289   fprintf(file, "\n");
00290 }
00291 
00292 
00293 static SMat svdLoadSparseTextFile(FILE *file) {
00294   long c, i, n, v, rows, cols, vals;
00295   SMat S;
00296   if (fscanf(file, " %ld %ld %ld", &rows, &cols, &vals) != 3) {
00297     svd_error("svdLoadSparseTextFile: bad file format");
00298     return NULL;
00299   }
00300 
00301   S = svdNewSMat(rows, cols, vals);
00302   if (!S) return NULL;
00303   
00304   for (c = 0, v = 0; c < cols; c++) {
00305     if (fscanf(file, " %ld", &n) != 1) {
00306       svd_error("svdLoadSparseTextFile: bad file format");
00307       return NULL;
00308     }
00309     S->pointr[c] = v;
00310     for (i = 0; i < n; i++, v++) {
00311       if (fscanf(file, " %ld %lf", S->rowind + v, S->value + v) != 2) {
00312         svd_error("svdLoadSparseTextFile: bad file format");
00313         return NULL;
00314       }
00315     }
00316   }
00317   S->pointr[cols] = vals;
00318   return S;
00319 }
00320 
00321 static void svdWriteSparseTextFile(SMat S, FILE *file) {
00322   int c, v;
00323   fprintf(file, "%ld %ld %ld\n", S->rows, S->cols, S->vals);
00324   for (c = 0, v = 0; c < S->cols; c++) {
00325     fprintf(file, "%ld\n", S->pointr[c + 1] - S->pointr[c]);
00326     for (; v < S->pointr[c+1]; v++)
00327       fprintf(file, "%ld %g\n", S->rowind[v], S->value[v]);
00328   }
00329 }
00330 
00331 
00332 static SMat svdLoadSparseBinaryFile(FILE *file) {
00333   int rows, cols, vals, n, c, i, v, r, e = 0;
00334   float f;
00335   SMat S;
00336   e += svd_readBinInt(file, &rows);
00337   e += svd_readBinInt(file, &cols);
00338   e += svd_readBinInt(file, &vals);
00339   if (e) {
00340     svd_error("svdLoadSparseBinaryFile: bad file format");
00341     return NULL;
00342   }
00343 
00344   S = svdNewSMat(rows, cols, vals);
00345   if (!S) return NULL;
00346   
00347   for (c = 0, v = 0; c < cols; c++) {
00348     if (svd_readBinInt(file, &n)) {
00349       svd_error("svdLoadSparseBinaryFile: bad file format");
00350       return NULL;
00351     }
00352     S->pointr[c] = v;
00353     for (i = 0; i < n; i++, v++) {
00354       e += svd_readBinInt(file, &r);
00355       e += svd_readBinFloat(file, &f);
00356       if (e) {
00357         svd_error("svdLoadSparseBinaryFile: bad file format");
00358         return NULL;
00359       }
00360       S->rowind[v] = r;
00361       S->value[v] = f;
00362     }
00363   }
00364   S->pointr[cols] = vals;
00365   return S;
00366 }
00367 
00368 static void svdWriteSparseBinaryFile(SMat S, FILE *file) {
00369   int c, v;
00370   svd_writeBinInt(file, (int) S->rows);
00371   svd_writeBinInt(file, (int) S->cols);
00372   svd_writeBinInt(file, (int) S->vals);
00373   for (c = 0, v = 0; c < S->cols; c++) {
00374     svd_writeBinInt(file, (int) (S->pointr[c + 1] - S->pointr[c]));
00375     for (; v < S->pointr[c+1]; v++) {
00376       svd_writeBinInt(file, (int) S->rowind[v]);
00377       svd_writeBinFloat(file, (float) S->value[v]);
00378     }
00379   }
00380 }
00381 
00382 
00383 static DMat svdLoadDenseTextFile(FILE *file) {
00384   long rows, cols, i, j;
00385   DMat D;
00386   if (fscanf(file, " %ld %ld", &rows, &cols) != 2) {
00387     svd_error("svdLoadDenseTextFile: bad file format");
00388     return NULL;
00389   }
00390 
00391   D = svdNewDMat(rows, cols);
00392   if (!D) return NULL;
00393 
00394   for (i = 0; i < rows; i++)
00395     for (j = 0; j < cols; j++) {
00396       if (fscanf(file, " %lf", &(D->value[i][j])) != 1) {
00397         svd_error("svdLoadDenseTextFile: bad file format");
00398         return NULL;
00399       }
00400     }
00401   return D;
00402 }
00403 
00404 static void svdWriteDenseTextFile(DMat D, FILE *file) {
00405   int i, j;
00406   fprintf(file, "%ld %ld\n", D->rows, D->cols);
00407   for (i = 0; i < D->rows; i++)
00408     for (j = 0; j < D->cols; j++) 
00409       fprintf(file, "%g%c", D->value[i][j], (j == D->cols - 1) ? '\n' : ' ');
00410 }
00411 
00412 
00413 static DMat svdLoadDenseBinaryFile(FILE *file) {
00414   int rows, cols, i, j, e = 0;
00415   float f;
00416   DMat D;
00417   e += svd_readBinInt(file, &rows);
00418   e += svd_readBinInt(file, &cols);
00419   if (e) {
00420     svd_error("svdLoadDenseBinaryFile: bad file format");
00421     return NULL;
00422   }
00423 
00424   D = svdNewDMat(rows, cols);
00425   if (!D) return NULL;
00426 
00427   for (i = 0; i < rows; i++)
00428     for (j = 0; j < cols; j++) {
00429       if (svd_readBinFloat(file, &f)) {
00430         svd_error("svdLoadDenseBinaryFile: bad file format");
00431         return NULL;
00432       }
00433       D->value[i][j] = f;
00434     }
00435   return D;
00436 }
00437 
00438 static void svdWriteDenseBinaryFile(DMat D, FILE *file) {
00439   int i, j;
00440   svd_writeBinInt(file, (int) D->rows);
00441   svd_writeBinInt(file, (int) D->cols);
00442   for (i = 0; i < D->rows; i++)
00443     for (j = 0; j < D->cols; j++) 
00444       svd_writeBinFloat(file, (float) D->value[i][j]);
00445 }
00446 
00447 
00448 SMat svdLoadSparseMatrix(char *filename, int format) {
00449   SMat S = NULL;
00450   DMat D = NULL;
00451   FILE *file = svd_fatalReadFile(filename);
00452   switch (format) {
00453   case SVD_F_STH: 
00454     S = svdLoadSparseTextHBFile(file);
00455     break;
00456   case SVD_F_ST:
00457     S = svdLoadSparseTextFile(file);
00458     break;
00459   case SVD_F_SB:
00460     S = svdLoadSparseBinaryFile(file);
00461     break;
00462   case SVD_F_DT:
00463     D = svdLoadDenseTextFile(file);
00464     break;
00465   case SVD_F_DB:
00466     D = svdLoadDenseBinaryFile(file);
00467     break;
00468   default: svd_error("svdLoadSparseMatrix: unknown format %d", format);
00469   }
00470   svd_closeFile(file);
00471   if (D) {
00472     S = svdConvertDtoS(D);
00473     svdFreeDMat(D);
00474   }
00475   return S;
00476 }
00477 
00478 DMat svdLoadDenseMatrix(char *filename, int format) {
00479   SMat S = NULL;
00480   DMat D = NULL;
00481   FILE *file = svd_fatalReadFile(filename);
00482   switch (format) {
00483   case SVD_F_STH: 
00484     S = svdLoadSparseTextHBFile(file);
00485     break;
00486   case SVD_F_ST:
00487     S = svdLoadSparseTextFile(file);
00488     break;
00489   case SVD_F_SB:
00490     S = svdLoadSparseBinaryFile(file);
00491     break;
00492   case SVD_F_DT:
00493     D = svdLoadDenseTextFile(file);
00494     break;
00495   case SVD_F_DB:
00496     D = svdLoadDenseBinaryFile(file);
00497     break;
00498   default: svd_error("svdLoadSparseMatrix: unknown format %d", format);
00499   }
00500   svd_closeFile(file);
00501   if (S) {
00502     D = svdConvertStoD(S);
00503     svdFreeSMat(S);
00504   }
00505   return D;
00506 }
00507 
00508 void svdWriteSparseMatrix(SMat S, char *filename, int format) {
00509   DMat D = NULL;
00510   FILE *file = svd_writeFile(filename, FALSE);
00511   if (!file) {
00512     svd_error("svdWriteSparseMatrix: failed to write file %s\n", filename);
00513     return;
00514   }
00515   switch (format) {
00516   case SVD_F_STH: 
00517     svdWriteSparseTextHBFile(S, file);
00518     break;
00519   case SVD_F_ST:
00520     svdWriteSparseTextFile(S, file);
00521     break;
00522   case SVD_F_SB:
00523     svdWriteSparseBinaryFile(S, file);
00524     break;
00525   case SVD_F_DT:
00526     D = svdConvertStoD(S);
00527     svdWriteDenseTextFile(D, file);
00528     break;
00529   case SVD_F_DB:
00530     D = svdConvertStoD(S);
00531     svdWriteDenseBinaryFile(D, file);
00532     break;
00533   default: svd_error("svdLoadSparseMatrix: unknown format %d", format);
00534   }
00535   svd_closeFile(file);
00536   if (D) svdFreeDMat(D);
00537 }
00538 
00539 void svdWriteDenseMatrix(DMat D, char *filename, int format) {
00540   SMat S = NULL;
00541   FILE *file = svd_writeFile(filename, FALSE);
00542   if (!file) {
00543     svd_error("svdWriteDenseMatrix: failed to write file %s\n", filename);
00544     return;
00545   }
00546   switch (format) {
00547   case SVD_F_STH: 
00548     S = svdConvertDtoS(D);
00549     svdWriteSparseTextHBFile(S, file);
00550     break;
00551   case SVD_F_ST:
00552     S = svdConvertDtoS(D);
00553     svdWriteSparseTextFile(S, file);
00554     break;
00555   case SVD_F_SB:
00556     S = svdConvertDtoS(D);
00557     svdWriteSparseBinaryFile(S, file);
00558     break;
00559   case SVD_F_DT:
00560     svdWriteDenseTextFile(D, file);
00561     break;
00562   case SVD_F_DB:
00563     svdWriteDenseBinaryFile(D, file);
00564     break;
00565   default: svd_error("svdLoadSparseMatrix: unknown format %d", format);
00566   }
00567   svd_closeFile(file);
00568   if (S) svdFreeSMat(S);
00569 }