0

I'm currently trying to solve a particular eigenvalue problem (so-called gyroscopic eigenvalue problem) with large, sparse matrices (from FEM dsicretization). The programming language is C++.

The standard reference for EVP is ARPACK. Alas, it implements only "classical" Arnoldi processes, which is not suited for such problems (c.f. Structure Preserving Methods).

Recently I've found this Algorithm 961 reference, which also provides some code - in FORTRAN! So i've tried to include the DGHUTR routine in C++, to no avail. Below is the MWE, which is an adaptation of the test for DGHUTR (TDGHUTR.f) in C++:

#include <Eigen/Dense>
#include <Eigen/Sparse>
//definition stolen from ARPACK++
#define F77NAME(x) x ## _

//Interface to the SHEIG library function DGHUTR
#ifdef __cplusplus
extern "C"
{
#endif
  void F77NAME(dghutr)( char* JOB, char* COMPQ1, char* COMPQ2, int* N, double* A, int* LDA,
                        double* DE, int* LDDE, double* C1, int* LDC1, double* VW, int* LDVW,
                        double* Q1, int* LDQ1, double* Q2, int* LDQ2, double* B, int* LDB,
                        double* F, int* LDF, double* C2, int* LDC2, double* ALPHAR, double* ALPHAI,
                        double* BETA, int* IWORK, int* LIWORK, double* DWORK,int* LDWORK, int* INFO );
#ifdef __cplusplus
}
#endif


int main(void){
  // define system sizes
  int N(8),  M(N/2);
  std::cout << "Sizes: " << N << '\t' << M << std::endl;


  char job('E'),  compq1('I'),  compq2('I');
  int lda(M),  ldde(M),  ldq1(N),  ldq2(N),  ldb(M),  ldc1(M),  ldc2(M),  ldf(M),  ldvw(M);

  int ldwork = 2*N*N+std::max(4*N+4, 32);
  int liwork = N+12;


  // workspace arrays
  int* iwork = new int[liwork];
  double* dwork = new double[ldwork];
  int info(0);
  // auxiliary matrices and  vectors
  Eigen::MatrixXd F(ldf, M),  C2(ldc2, M),  Q1(ldq1, N),  Q2(ldq2, N),  B(ldb, M);
  Eigen::VectorXd alphaR(M),  alphaI(M),  beta(M);

  //matrices with data
  Eigen::MatrixXd A(lda,M), DE(ldde,M+1), C1(ldc1,M), VW(ldvw,M+1);

     A << 3.1472,   1.3236,   4.5751,   4.5717,
   4.0579,  -4.0246,   4.6489,  -0.1462,
  -3.7301,  -2.2150,  -3.4239,   3.0028,
   4.1338,   0.4688,   4.7059,  -3.5811;

   DE << 0.0000,   0.0000,  -1.5510,  -4.5974,  -2.5127,
   3.5071,   0.0000,   0.0000,   1.5961,   2.4490,  
  -3.1428,   2.5648,   0.0000,   0.0000,  -0.0596, 
   3.0340,   2.4892,  -1.1604,   0.0000,   0.0000;

   C1 <<  0.6882,  -3.3782,  -3.3435,   1.8921,
  -0.3061,   2.9428,   1.0198,   2.4815,
  -4.8810,  -1.8878,  -2.3703,  -0.4946,
  -1.6288,   0.2853,   1.5408,  -4.1618;

   VW <<  -2.4013,  -2.7102,   0.3834,  -3.9335,   3.1730,
  -3.1815,  -2.3620,   4.9613,   4.6190,   3.6869,
   3.6929,   0.7970,  0.4986,  -4.9537,  -4.1556,
   3.5303,   1.2206,  -1.4905,   0.1325,  -1.0022;

  /* outputs of each parameter save for dwork,iwork to check correctness. */

  F77NAME(dghutr)( &job, &compq1, &compq2, &N, A.data(), &lda, DE.data(), &ldde,  C1.data(), &ldc1, VW.data(), &ldvw,
                         Q1.data(), &ldq1,  Q2.data(), &ldq2,  B.data(), &ldb,
                         F.data(), &ldf,  C2.data(), &ldc2, alphaR.data(),  alphaI.data(),
                         beta.data(), iwork, &liwork, dwork, &ldwork, &info );
  std::cout << "result: " << info << std::endl;
  delete[] iwork;
  delete[] dwork;
}

Compilation is done with (it uses a lot of other stuff):

g++ -o eigensolver EigenSHEIGSolver.cpp -I/home/shared/eigen-eigen-1306d75b4a21  /home/shared/SHIRA/SHEVP/src/shheig64.a /home/shared/SHIRA/SLICOT_Lib/slicot64.a /home/shared/SHIRA/SLICOT_Lib/lpkaux64.a /home/shared/ATLAS/builddir/lib/libptlapack.a /home/shared/ATLAS/builddir/lib/libptcblas.a /home/shared/ATLAS/builddir/lib/libptf77blas.a /home/shared/ATLAS/builddir/lib/libatlas.a /home/shared/ATLAS/builddir/lib/libptcblas.a -lgfortran -lpthread

Alas, whenever I run the resulting executable it gives me:

 ** On entry to DGHUTR parameter number  8 had an illegal value

My FORTRAN knowledge is extremely limited and the above code was written mainly using YoLinux Tutorial mixing FORTRAN and C and CRAY Docs as references. As I understand it the routine reports an error with the ldde variable. I've got no clue why, though.

Can anyone shed some light on this for me, please?

N.B. As per Eigen Docs: storage order Eigen stores matrices per default in col-major order, thus it should be interfaceable with FORTRAN. And the FORTRAN subroutine DGHUTR is

SUBROUTINE DGHUTR( JOB, COMPQ1, COMPQ2, N, A, LDA, DE, LDDE, C1,
 $                   LDC1, VW, LDVW, Q1, LDQ1, Q2, LDQ2, B, LDB, F,
 $                   LDF, C2, LDC2, ALPHAR, ALPHAI, BETA, IWORK,
 $                   LIWORK, DWORK, LDWORK, INFO )

Update: Here's the output of the modified DGHUTR subroutine (basically added printing):

 JOB T
 COMPQ1 I
 COMPQ2 I
 LDA          17179869188
 LDDE          34359738372
 LDC1          17179869188
 LDVW         704374636548
 LDQ1          34359738376
 LDB          17179869188
 LDF          17179869188
 LDC2          17179869188
 LIWORK                   20
 LDWORK          85899346084
 N          17179869192
 LDDE          34359738372
 INFO  6227620798727716864

As one can see the characters are received correctly, as is LIWORK, provided I compile with -O2 set. I'm guessing there's something g++ does which breaks the parameters. Trying to revert from gcc-5 to gcc-4.8 did not solve the issue. With no optimization the LDA value seems to be changing on each run of the program, whereas it remains fixed when compiled with -O2.

Nox
  • 324
  • 3
  • 18
  • Parameter number 8 is `LDDE`. It would be worth to just add a `print` statement into `DGHUTR` to see which value it is actually seeing and how it differs from the value you are trying to pass. Also check the value you are passing from C++ is actually correct in the documentation of `DGHUTR`. – Vladimir F Героям слава Feb 23 '17 at 10:26
  • Done, the output is (N, LDA, LDDE...): 137438953480 4992602798144094212 17179869188 17179869188 17179869188 17179869192 34359738372 17179869188 17179869188 704374636564 17179869348 Which is nonsensical! – Nox Feb 23 '17 at 10:58
  • Ok. On an interesting note: I recompiled the code with `-O2` set and suddenly `LIWORK` has the correct value of `20`, the rest is still garbage thoughand the routine promptly reports parameter 27 (LIWORK) having an illegal value – Nox Feb 23 '17 at 11:57
  • Weird. When I build it (even without `-O2`) I get the expected values. If there is any garbage then expect some random error, as the first thing `DGHUTR` does is try to validate the parameters. I also don't see how/where you have any sparse matrices. – Avi Ginsburg Feb 23 '17 at 12:18
  • There are no sparse matrices yet. I was just thinking of using this routine for that, but as far as I see this one requires dense matrices so it'll be a no-go - probably. I've recompiled the library with system BLAS and Lapack (`-llapack -lcblas -lblas`) and it still yields the same error. – Nox Feb 23 '17 at 12:33
  • Have you a spec where is defined what `DGHUTR` expects as parameter(-types) in FORTRAN? – Alex Feb 24 '17 at 18:13
  • Due to the Algorithm 961 paper and source being paywalled I've found this one for public use: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.401.5474&rep=rep1&type=pdf – Nox Feb 24 '17 at 18:25

1 Answers1

0

I think I have found the source of the problem which has been plaguing me. The dependence of the values received by the fortran routine on the optimization flags was kind of a hint that there might be something wrong with how the stored variables are interpreted by C++ and FORTRAN. After looking for the specific value of 17179869188 and finding this SO post I tried playing with the compiler flags for the libraries.

When I fetched SLICOT I took the source and a library precompiled with gfortran for linux (slicot_linux_gfortran.tar.gz). This latter one came with a make.inc with OPTS = -O2 -fpic -fdefault-integer-8 The SHHEVP routines contained the following comment in the make.inc

IMPORTANT: Use the options -fPIC -fdefault-integer-8 for 64bit
architectures.

So I did as advised - and that was the problem!

Removing -fdefault-integer-8 and recompiling both SLICOT and DGHUTR solved my problem. Now the code given above compiles and the FORTRAN subroutine receives the correct values. The results of the computation are consistent with the reference results provided with the DGHUTR source.

Incidentally, the majority of the SLICOT tests are now working. With the old flags the compilation of examples stopped at TAB01ND, which would always hang. Now I get down to TMB03LD, whose compilation fails with

IF( LSAME( COMPQ, 'C' ) .AND. NEIG.GT.0 ) THEN              
                             1
Error: Operands of logical operator '.and.' at (1) are INTEGER(4)/LOGICAL(4)

But that is, for now, of no concern to me.

Nox
  • 324
  • 3
  • 18