common_umfpack.c

Go to the documentation of this file.
00001 /*
00002  *   Copyright Bruno Pinçon, ESIAL-IECN, Inria CORIDA project 
00003  *   <bruno.pincon@iecn.u-nancy.fr>
00004  *   contributor:  Antonio Manoel Ferreria Frasson, Universidade Federal do 
00005  *                 Espírito Santo, Brazil. <frasson@ele.ufes.br>.
00006  *
00007  * PURPOSE: Scilab interfaces routines onto the UMFPACK sparse solver
00008  * (Tim Davis) and onto the TAUCS snmf choleski solver (Sivan Teledo)
00009  *
00010  * This software is governed by the CeCILL license under French law and
00011  * abiding by the rules of distribution of free software.  You can  use,
00012  * modify and/or redistribute the software under the terms of the CeCILL
00013  * license as circulated by CEA, CNRS and INRIA at the following URL
00014  * "http://www.cecill.info".
00015  *
00016  * As a counterpart to the access to the source code and  rights to copy,
00017  * modify and redistribute granted by the license, users are provided only
00018  * with a limited warranty  and the software's author,  the holder of the
00019  * economic rights,  and the successive licensors  have only  limited
00020  * liability.
00021  *
00022  * In this respect, the user's attention is drawn to the risks associated
00023  * with loading,  using,  modifying and/or developing or reproducing the
00024  * software by the user in light of its specific status of free software,
00025  * that may mean  that it is complicated to manipulate,  and  that  also
00026  * therefore means  that it is reserved for developers  and  experienced
00027  * professionals having in-depth computer knowledge. Users are therefore
00028  * encouraged to load and test the software's suitability as regards their
00029  * requirements in conditions enabling the security of their systems and/or
00030  * data to be ensured and,  more generally, to use and operate it in the
00031  * same conditions as regards security.
00032  *
00033  * The fact that you are presently reading this means that you have had
00034  * knowledge of the CeCILL license and that you accept its terms.
00035  *
00036  */
00037 /*
00038  * First here are some utilities for the interface : 
00039  *
00040  *   1) When a user computes an LU fact with umf_lufact or a Choleski
00041  *      fact with taucs_chfact he/she get at the scilab level only a 
00042  *      pointer on to the factorization : the memory for this factorization 
00043  *      is outside scilab memory. In order to avoid problem with scilab 
00044  *      pointer coming from other usage I manage in this interface a 
00045  *      linked list of all the valid pointers onto "umfpack numerical 
00046  *      factorisation handle" and the same for the Choleski factorizations. 
00047  *      These 2 lists are called 
00048  *
00049  *         ListNumeric
00050  *         ListCholFactor
00051  *
00052  *      And so you will find here 3 routines to deal with :
00053  *
00054  *         AddAdrToList, RetrieveAdrFromList, IsAdrInList
00055  *
00056  *   2) some others  utility routines are used :
00057  *
00058  *         sci_sparse_to_ccs_sparse: scilab sparse format -> CCS format (for umfpack)
00059  *
00060  *         spd_sci_sparse_to_taucs_sparse: transform a scilab sparse (supposed to be spd)
00061  *                                         in the format wait by taucs 
00062  *
00063  *         TransposeMatrix: used only to solve x = b/A  (umfpack(b,"/",A)
00064  *
00065  *         UmfErrorMes : to deal with some few messages from umfpack
00066  *
00067  *         test_size_for_sparse : to see if stk have enough places to 
00068  *                                store a sparse (see comments)
00069  *         test_size_for_mat : the same for classic matrix
00070  */
00071 
00072 #include "MALLOC.h"
00073 #include "stack-c.h"
00074 #include "sciumfpack.h"
00075 #include "taucs_scilab.h"
00076 #include "common_umfpack.h"
00077 #include "localization.h"
00078 
00079 int AddAdrToList(Adr adr, int it_flag, CellAdr **L)
00080 {
00081   CellAdr *NewCell;
00082   if ( (NewCell = MALLOC(sizeof(CellAdr))) == NULL )
00083     return 0;
00084   else  
00085     {
00086       NewCell->adr = adr;
00087       NewCell->it = it_flag;
00088       NewCell->next = *L;
00089       *L = NewCell;
00090       return 1;
00091     }
00092 }
00093 
00094 int RetrieveAdrFromList(Adr adr, CellAdr **L, int *it_flag)
00095 {
00096   /* teste si l'adresse adr est presente ds la liste L, si oui
00097      on la retire et la fonction renvoie 1, sinon 0  */
00098   CellAdr * Cell;
00099 
00100   if ( *L == NULL )
00101     return 0;
00102   else if ( (*L)->adr == adr ) 
00103     {
00104       Cell = *L;
00105       *it_flag = Cell->it;
00106       *L = (*L)->next;
00107       FREE(Cell);
00108       return 1;
00109     }
00110   else
00111     return ( RetrieveAdrFromList(adr, &((*L)->next), it_flag));
00112 }
00113 
00114 int IsAdrInList(Adr adr, CellAdr *L, int *it_flag)
00115 {
00116   /* teste si l'adresse adr est presente ds la liste L, si oui
00117      la fonction renvoie 1, sinon 0. On renvoit aussi it */
00118 
00119   if ( L == NULL )
00120     return 0;
00121   else if ( L->adr == adr ) 
00122     {
00123       *it_flag = L->it;
00124       return 1;
00125     }
00126   else
00127     return ( IsAdrInList(adr, L->next, it_flag) );
00128 }
00129 
00130 
00131 void TransposeMatrix(double A[], int ma, int na, double At[])
00132 {
00133   /*  compute At the (na,ma) matrix gotten by the
00134    *  transposition of the (ma, na) matrix A.  A and
00135    *  At are in fact simple 1-d arrays with the fortran 
00136    *  storage convention :
00137    *     A(i,j) = A[i + ma*j] , At(i,j) = At[i + na*j]
00138    *
00139    *     At(i,j) = A(j,i) = A[j + ma*i]
00140    */
00141   int i, j;
00142   for ( j = 0 ; j < ma ; j++ )
00143     for ( i = 0 ; i < na ; i++ )
00144       At[i + na*j] = A[j + ma*i];
00145 }
00146 
00147 int sci_sparse_to_ccs_sparse(int num, SciSparse *A, CcsSparse *B)
00148 {
00149   int taille, one=1, nel = A->nel, m = A->m, n = A->n, it = A->it;
00150   int l0, l1, l2, k, kb, i, j, count;
00151 
00152   taille = ((2*it+3)*nel + n+1 )/2 + 2;
00153 
00154   CreateVar(num, MATRIX_OF_DOUBLE_DATATYPE, &taille, &one, &l0);  /* return 0 in case of failure (not enough memory in stk) */
00155 
00156   B->m = m; B->n = n; B->nel = nel; B->it = it;
00157   B->R = stk(l0);
00158   if ( it == 1 )
00159     {
00160       B->I = stk(l0 + nel); l1 = l0 + 2*nel;
00161     }
00162   else
00163     {
00164       B->I = NULL; l1 = l0 + nel;
00165     }
00166   B->p = (int *) stk(l1); l2 = l1 + (n+1)/2 + 1;
00167   B->irow = (int *) stk(l2);
00168 
00169   for ( i = 0 ; i <= n ; i++ )
00170     B->p[i] = 0;
00171 
00172   for ( k = 0 ; k < nel ; k++ )
00173     B->p[A->icol[k]]++;   /* this is because  A->icol[k] is 1-based (and not 0-based) */
00174 
00175   for ( i = 2 ; i <= n ; i++ )
00176     B->p[i] += B->p[i-1];
00177 
00178   k = 0;
00179   for ( i = 0 ; i < m ; i++ )
00180     for ( count = 0 ; count < A->mnel[i] ; count++ )
00181       {
00182         j = A->icol[k]-1;
00183         kb = B->p[j];  /* "pointeur" actuel sur la colonne j */
00184         B->irow[kb] = i;
00185         B->R[kb] = A->R[k];
00186         if (it == 1) B->I[kb] = A->I[k];
00187         B->p[j]++;
00188         k++;
00189       }
00190 
00191   for ( i = n-1 ; i > 0 ; i-- )
00192     B->p[i] = B->p[i-1];
00193   B->p[0] = 0;
00194 
00195   return 1;
00196 }
00197 
00198 char *UmfErrorMes(int num_error)
00199 {
00200   /* to deal with various umfpack error indicator */
00201   char *mes1 = _("singular matrix");
00202   char *mes2 = _("not enough memory");
00203   char *mes3 = _("internal error");
00204   char *mes4 = _("invalid matrix");
00205   /*  normallly with the different controls in the interface
00206    *  the others errors may not occured but we put anyway
00207    *  this last one :
00208    */
00209   char *mes5 = "unidentified error";
00210 
00211   switch (num_error) {
00212   case UMFPACK_WARNING_singular_matrix: return(mes1);
00213   case UMFPACK_ERROR_out_of_memory:   return(mes2);
00214   case UMFPACK_ERROR_internal_error:  return(mes3);
00215   case UMFPACK_ERROR_invalid_matrix:  return(mes4);
00216   default:                            return(mes5);
00217   };
00218 }
00219 
00220 int is_sparse_upper_triangular(SciSparse *A)
00221 {
00222   int i, k=0, nb_elem_row_i;
00223   for ( i = 0 ; i < A->m ; i++ )
00224     {
00225       nb_elem_row_i = A->mnel[i];
00226       if (nb_elem_row_i > 0  &&  A->icol[k] <= i)
00227         return 0; 
00228       k += nb_elem_row_i;
00229     }
00230   return 1;
00231 }
00232 
00233 int spd_sci_sparse_to_taucs_sparse(int num, SciSparse *A, taucs_ccs_matrix *B)
00234 {
00235   /*  
00236    *  this function create a taucs sparse symetric matrix (with only the lower
00237    *  triangle part) from a supposed symetric spd scilab sparse one's.
00238    *  This is to put a sci sparse in the format wait by taucs routines
00239    *
00240    *  The scilab sparse may be only upper triangular
00241    */
00242   int taille, one=1, B_nel, n = A->n;
00243   int l0, l1, l2, i, k, l, p, nnz;
00244 
00245   if ( A->m != A->n  ||  A->m <= 0  ||  A->it == 1 )
00246     return ( MAT_IS_NOT_SPD );
00247 
00248   if ( is_sparse_upper_triangular(A) )
00249     B_nel = A->nel;
00250   else
00251     B_nel = n + (A->nel - n)/2;
00252   taille = (3*B_nel + n+1 )/2 + 2;
00253 
00254   CreateVar(num, MATRIX_OF_DOUBLE_DATATYPE, &taille, &one, &l0);  /* return 0 in case of failure (not enough memory in stk) */
00255 
00256   /* form the taucs sparse matrix struct */
00257   B->m = n; B->n = n;
00258   B->flags =  TAUCS_SYMMETRIC | TAUCS_LOWER;
00259 
00260   B->values = stk(l0); l1 = l0 + B_nel;
00261   B->colptr = (int *) stk(l1); l2 = l1 + (n+1)/2 + 1;
00262   B->rowind = (int *) stk(l2);
00263 
00264   nnz = 0;
00265   k = 0;
00266   for ( i = 0 ; i < n ; i++ )
00267     {
00268       if ( A->mnel[i] > 0 )
00269         {
00270           l = 0;
00271           while ( l < A->mnel[i]  &&  A->icol[k+l] < i+1 )   /* go to the diagonal element */ 
00272             l++;
00273           if ( l >= A->mnel[i] )          /* no element A_ij with j >= i => A_ii = 0  */
00274             return ( MAT_IS_NOT_SPD );
00275           else if ( A->icol[k+l] > i+1 )  /* A_ii = 0 */
00276             return ( MAT_IS_NOT_SPD );
00277           else if ( A->R[k+l] <= 0 )      /* A_ii <= 0 */
00278             return ( MAT_IS_NOT_SPD );
00279           else                            /* normal case A_ii > 0 */
00280             {        
00281               if ( nnz + A->mnel[i] - l > B_nel )    
00282                 return ( MAT_IS_NOT_SPD );
00283               B->colptr[i] = nnz;
00284               for ( p = l ; p < A->mnel[i] ; p++)
00285                 {
00286                   B->values[nnz] = A->R[k+p];
00287                   B->rowind[nnz] = A->icol[k+p]-1;
00288                   nnz++;
00289                 }
00290               k = k + A->mnel[i];
00291             }
00292         }
00293       else
00294         return ( MAT_IS_NOT_SPD );
00295     }
00296   if ( nnz != B_nel )
00297     return ( MAT_IS_NOT_SPD );
00298 
00299   B->colptr[n] = nnz;
00300   
00301   return ( A_PRIORI_OK );
00302 }
00303 
00304 
00305 /*------------------------------------------------------*
00306  * an utility to test if we can create a sparse matrix  *
00307  * in the scilab stack                                  *
00308  *------------------------------------------------------*/
00309 
00310 int test_size_for_sparse(int pos, int m, int it, int nel, int * pl_miss) 
00311 {
00312   /*  test if the scilab stack can currently store at the
00313    *  position pos a sparse matrix with m rows and nel 
00314    *  non nul elements (Bruno le 17/12/2001 with the help
00315    *  of jpc). This function is required because with a failure
00316    *  in a CreateVarFromPtr(pos, "s", ...) the control is then
00317    *  passed (via Scierror) to the intepretor and we can lose
00318    *  the pointer and so don't be able to free the associated
00319    *  memory to this pointer 
00320    */
00321  
00322   int lw = pos + Top - Rhs, il;
00323 
00324   if (lw + 1 >= Bot) return FALSE; /* even no place for a supplementary var */
00325 
00326   /* 5 + m + nel : number of "integers" cases required for the sparse */
00327   
00328   il = iadr(*Lstk(lw )) +  5 + m + nel;
00329   *pl_miss =  (sadr(il) + nel*(it+1)) - *Lstk(Bot); 
00330 
00331   if ( *pl_miss > 0 )
00332     return FALSE;
00333   else
00334     return TRUE;
00335 }
00336 
00337 int test_size_for_mat(int pos, int m, int n, int it, int * pl_miss) 
00338 {
00339   /* the same for classic matrix (trick given par jpc) */
00340   int lw = pos + Top - Rhs, il;
00341 
00342   if (lw + 1 >= Bot) return FALSE;
00343 
00344   /* 4 is the number of integer "cases" required for a classic matrix
00345    * (type , m, n, it)
00346    */
00347   il = iadr(*Lstk(lw )) + 4;
00348 
00349   *pl_miss =  (sadr(il) + m*n*(it+1)) - *Lstk(Bot); 
00350 
00351   if ( *pl_miss > 0 )
00352     return FALSE;
00353   else
00354     return TRUE;
00355 }
00356 
00357 
00358 void residu_with_prec(SciSparse *A, double x[], double b[], double r[], double *rn)
00359 {
00360   /*  un essai de calcul de residu : r = Ax - b, en precision double etendue */
00361   int i, j, k, l;
00362   long double temp, norm2;
00363 
00364   norm2 = 0.0;
00365   k = 0;
00366   for ( i = 0 ; i < A->m ; i++ )
00367     {
00368       temp = 0.0;
00369       for ( l = 0 ; l < A->mnel[i] ; l++ )
00370         {
00371           j = A->icol[k] - 1;
00372           temp += (long double) A->R[k]  *  (long double) x[j];
00373           k++;
00374         }
00375       temp -=  (long double) b[i];
00376       r[i] = (double) temp;
00377       norm2 += temp*temp;
00378     }
00379   *rn = (double) sqrt(norm2);
00380   return;
00381 }
00382 
00383 void residu_with_prec_for_chol(SciSparse *A, double x[], double b[], double r[], 
00384                                       double *rn, int A_is_upper_triangular, long double wk[])
00385 {
00386   /*  the same than the previous routine but this one take care of the fact that 
00387    *  when A_is_upper_triangular=1 only the upper triangle part of A is stored */
00388   int i, j, k, l;
00389   long double norm2 = 0.0;
00390 
00391   if ( ! A_is_upper_triangular )
00392     residu_with_prec(A, x, b, r, rn);
00393   else
00394     {   /* A*x-b but only the upper triangle of A is stored */
00395       for ( i = 0 ; i < A->m ; i++ ) 
00396         wk[i] = -(long double) b[i];
00397       k = 0;
00398       for ( i = 0 ; i < A->m ; i++ )
00399         {
00400           for ( l = 0 ; l < A->mnel[i] ; l++ )
00401             {
00402               j = A->icol[k] - 1;
00403               wk[i] += (long double) A->R[k]  *  (long double) x[j];
00404               if ( j != i )
00405                 wk[j] += (long double) A->R[k]  *  (long double) x[i];
00406               k++;
00407             }
00408         }
00409       for ( i = 0 ; i < A->m ; i++ ) 
00410         {
00411           r[i] = (double) wk[i];
00412           norm2 += wk[i]*wk[i];
00413         }
00414       *rn = (double) sqrt(norm2);
00415     }
00416   return;
00417 }
00418 
00419 
00420 void cmplx_residu_with_prec(SciSparse *A, 
00421                                    double xr[], double xi[],
00422                                    double br[], double bi[], 
00423                                    double rr[], double ri[],
00424                                    double *rn)
00425 {
00426   /*  the same for complex system */
00427   int i, j, k, l;
00428   long double tempr, tempi, norm2;
00429 
00430   norm2 = 0.0;
00431   k = 0;
00432   for ( i = 0 ; i < A->m ; i++ )
00433     {
00434       tempr = 0.0; tempi = 0.0;
00435       for ( l = 0 ; l < A->mnel[i] ; l++ )
00436         {
00437           j = A->icol[k] - 1;
00438           tempr +=   (long double) A->R[k]  *  (long double) xr[j]
00439                    - (long double) A->I[k]  *  (long double) xi[j];
00440           tempi +=   (long double) A->I[k]  *  (long double) xr[j]
00441                    + (long double) A->R[k]  *  (long double) xi[j];
00442           k++;
00443         }
00444       tempr -=  (long double) br[i];
00445       tempi -=  (long double) bi[i];
00446       rr[i] = (double) tempr;
00447       ri[i] = (double) tempi;
00448       norm2 += tempr*tempr + tempi*tempi;
00449     }
00450   *rn = (double) sqrt(norm2);
00451   return;
00452 }

Generated on Tue Sep 9 17:48:35 2008 for Scilab [trunk] by  doxygen 1.5.5