scalapack矩阵对角化(pdsyevd)

时间:2013-12-20 15:06:37

标签: c scalapack

我正在使用ScaLAPACK在C中的分而治之算法PDSYEVD编写一个用于并行矩阵对角化的小测试代码。但我是ScaLAPACK的新手并且在查看源代码时,它看起来是一个相当可怕的变量数量。设置为我找不到任何好的例子。我需要对角化的矩阵类型是真实的,对称的,相当稀疏的,大小约为1000x1000。

基于LAPACK的简单(串行)代码如下所示:

/* "matrix diagonalization using lapack" */
#include <stdio.h>
#include <stdlib.h>

/* DSYEV prototype */
extern void dsyev_(char* jobz, char* uplo, int* n, double* a, int* lda, 
                double* w, double* work, int* lwork, int* info );
#define n 4     

int main(int argc, char *argv[])
{
  int i,j,info,lda,lwork,N;
  double A[n][n], a[n*n], work[100*n],w[n];

  /* initialize matrices */
  for(i=0;i<n;i++) { for(j=0;j<n;j++) A[i][j] = (i+j); }

  /* diagonalize */
  N=n;  lda=n;  lwork=10*n;  info=0;
  for(i=0;i<n;i++) { for(j=0;j<n;j++) a[i+j*n]=A[i][j]; }
  dsyev_("V","L",&N,a,&lda,w,work,&lwork,&info);

  /* print result */
  for(i=0;i<n;i++) {
    printf("w[%d] = %8.4f  | v[%d] = [ ",i,w[i],i);
    for(j=0;j<n;j++) printf("%6.2f ",a[j+i*n]);
    printf("]\n");
  }

  return 0;
}

从那里我想去pdsyevd,应该被称为:

SUBROUTINE PDSYEVD( JOBZ, UPLO, N, A, IA, JA, DESCA, W, Z, IZ, JZ, DESCZ, WORK, LWORK, IWORK, LIWORK, INFO )

我不清楚一些事情。对这些问题中的任何一个或几个问题的答案将非常感激。

  1. 有没有人碰巧有一个例子?
  2. 在ScaLAPACK文档中,它说:“[...] PDSYEVD假定一个同类系统,异构系统可能会返回不正确的结果而没有任何错误消息。”在这种背景下,它是什么意思,一个“同质/异构”系统?
  3. 在文档的最后,它说:“分布式子矩阵必须验证一些对齐属性,即[...]”。这个表达式中的术语甚至不在输入中,我怎么知道这是否满足? MB_A / MB_Z?
  4. 如何为每个流程选择子矩阵?有什么理由吗?我认为它们的大小应该大致相等,但行/列/方块/ ......?对于更稀疏的区域,可能更大......?
  5. Z / IZ / IJ / DESCZ在输入中做什么?我已经给每个进程一个子矩阵A / IA / JA / DESCA来处理,为什么这个Z?它似乎是部分特征向量矩阵,但为什么不像DSYEV那样写入A?
  6. 所有进程是否必须具有相同的块大小?例如,子矩阵可以重叠,或者我对素数大小的矩阵做了什么?
  7. 什么是DESCA?什么是DLEN?它似乎是A的输入“数组描述符”。我不知道该怎么做。
  8. 到目前为止,这或多或少都是我所拥有的,但效果不是很好:

    /* "matrix diagonalization using scalapack" */
    #include <mpi.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    
    /* PDSYEVD prototype */
    extern void pdsyevd_( char* jobz, char* uplo, int* n, double* a, int* ia, int* ja, int* desca,
                    double* w, double* z, int* iz, int* jz, int* descz, double* work,
                    int* lwork, double* iwork, int* liwork, int* info );
    
    #define n 4
    
    int main(int argc, char *argv[])
    {
      int numprocs,myid,i,j,k,index,info,lwork,liwork,N;
      int ia,ja,desca[n];
      int iz,jz,descz[n];
      double A[n][n],w[n];
      double *a,*z,*work,*iwork;
    
      /* set up  MPI stuff */
      MPI_Init(&argc,&argv);                    // initialize MPI
      MPI_Comm_size(MPI_COMM_WORLD,&numprocs);  // find out size of world
      MPI_Comm_rank(MPI_COMM_WORLD,&myid);      // determine current mpi proc id
    
      /* initialize matrices */
      for(i=0;i<n;i++) { for(j=0;j<n;j++) A[i][j] = (i+j); }
    
      /* determine submatrices */
    
      /* let's use square blocks, of equal size:
         there number of processors (mp^2) should be
          - square (mp^2 = 1,2,4,9,16,..)
          - with mp being a divisor of n
    
         In case we have 4 procs and a 4x4 matrix for example
    
             (A11  A12  A13  A14)
             (A21  A22  A23  A24)
         A = (A31  A32  A33  A34)
             (A41  A42  A43  A44)
    
             ( a1  a2 )
           = ( a3  a3 )
    
         where      (A11  A12)
               a1 = (A21  A22)
    
         is the submatrix sent to process 1 for example 
    
        in this case :
          n=4
          nprocs=4 --> mp=2
          len = 2
        */
    
      int mp = sqrt(numprocs);
      if(numprocs!=mp*mp) {printf("ERROR: numprocs (%d) should be square.\n",numprocs); return 1; }
      if(n%mp!=0) {printf("ERROR: mp (%d) should be divisor of n (%d).\n",mp,n); return 1; }
      int len = n/mp;
      a = malloc((n*n+1)*sizeof(double));
      z = malloc((n*n+1)*sizeof(double));
      //a = malloc((len*len+1)*sizeof(double));
      //z = malloc((len*len+1)*sizeof(double));
    
      ia=(myid*len)%n; // from 0 to n in steps of len
      ja=(int)(myid/mp)*len;
      printf("proc %d has a submatrix of size %d, with starting indices (%d,%d)\n",myid,len,ia,ja);
    
      iz=ia;
      jz=ja;
    
      for(i=ia;i<ia+len;i++) { for(j=ja,k=0;j<ja+len;j++,k++) a[k]=A[i][j]; }
      for(i=iz;i<iz+len;i++) { for(j=jz,k=0;j<jz+len;j++,k++) z[k]=A[i][j]; }
    
      /* diagonalize */
      N=n;  lwork=n*n;  liwork=n*n;  info=0;
      work = malloc(lwork*sizeof(double));
      iwork = malloc(liwork*sizeof(double));
    
      for(i=0;i<n;i++) { for(j=0;j<n;j++) a[i+j*n]=A[i][j]; }
      pdsyevd_("V","U",&len,a,&ia,&ja,desca,w,z,&iz,&jz,descz,work,&lwork,iwork,&liwork,&info);
      if(!myid)printf("info = %d\n",info);
    
      /* gather & print results */
      double wf[n];
      MPI_Reduce(w,wf,n,MPI_DOUBLE,MPI_SUM,0,MPI_COMM_WORLD);
      if(myid==0) {
        for(i=0;i<n;i++) {
          printf("wf[%d] = %8.4f  | v[%d] = [ ",i,wf[i],i);
          for(j=0;j<n;j++) printf("%6.2f ",a[j+i*n]);
          printf("]\n");
        }
      }
    
      free(a); free(z); free(work); free(iwork);
    
      /* clean up */
      MPI_Finalize(); /* MPI Programs end with MPI Finalize; this is a weak synchronization point */
      return 0;
    }
    

1 个答案:

答案 0 :(得分:3)

两个有用的参考资料:

我相信IBM文档中描述的行为与ScaLAPACK匹配,而更完整地记录。

对于工作,工作,工作等:设置lwork = 0并且它们应该根据需要由子程序在内部分配,不需要传入它们。

对于z,iz,jz等:如果jobz ='V',则z包含“全局矩阵Z的更新的局部部分,其中全局矩阵Z的列jz到jz + n-1包含全局矩阵A “的正交特征向量。如果jobz!='V'则不使用这些。

更多问题的答案:

  1. 没有例子,对不起。
  2. http://en.wikipedia.org/wiki/System_of_linear_equations#Homogeneous_systems
  3. 中的同构系统
  4. 对不起,我不知道如何回答这个问题。
  5. 具有大致相等数量的非零元素的正方形。大致相同大小的方块也可能没问题。
  6. 上面和我链接的文档中描述了Z的用法。我认为它是为实施者提供便利。
  7. 不,任何块大小都应该没问题。我猜测将大小不等的矩阵分成两个略微不相等的部分。
  8. DESCA是一个由9个整数组成的数组,包含各种大小(全局矩阵行/列,块行/列等),在IBM文档中有描述。