Eigenvalue calculation using TQLI algorithm fails with segmentation fault

丶灬走出姿态 提交于 2019-12-13 03:50:35

问题


I am trying to calculate eigenvalues using the TQLI algorithm that I got from the website of the CACS of the University of Southern California. My test script looks like this:

#include <stdio.h>

int main()
{
    int i;

    i = rand();

    printf("My random number: %d\n", i);

    float d[4] = {
        {1, 2, 3, 4}    
    };

    float e[4] = {
        {0, 0, 0, 0}    
    };

    float z[4][4] = {  
        {1.0, 0.0, 0.0, 0.0} ,   
        {0.0, 1.0, 0.0, 0.0} ,   
        {0.0, 0.0, 1.0, 0.0},
        {0.0, 0.0, 0.0, 1.0}
    };

    double *zptr;
    zptr = &z[0][0];

    printf("Element [2][1] of identity matrix: %f\n", z[2][1]);
    printf("Element [2][2] of identity matrix: %f\n", z[2][2]);

    tqli(d, e, 4, zptr);

    printf("First eigenvalue: %f\n", d[0]);

    return 0;
}

When I try to run this script I get a segmentation fault error as you can see in here. At what location does my code produce this segmentation fault. As I believe the code from USC is bug-free I am pretty sure the mistake must be in my call of the function. However I can't see where I made a mistake in my set-up of the arrays as in my opinion I followed the instructions.


回答1:


Eigenvalue calculation using TQLI algorithm fails with segmentation fault

Segmentation fault comes from crossing the supplied array boundary. tqli requires specific data preparation.

1) The eigen code from CACS is Fortran based and counts indexes from 1.

2) The tqli expects double pointer for its matrix and double vectors.

/******************************************************************************/
void tqli(double d[], double e[], int n, double **z)
/*******************************************************************************

d, and e should be declared as double.

3) The program needs modification in respect to the data preparation for the above function.

Helper 1-index based vectors have to be created to supply properly formatted data for the tqli:

    double z[NP][NP] = { {2, 0, 0}, {0, 4, 0}, {0, 0, 2} } ;
    double **a;
    double *d,*e,*f;

    d=dvector(1,NP); // 1-index based vector
    e=dvector(1,NP);
    f=dvector(1,NP);

    a=dmatrix(1,NP,1,NP); // 1-index based matrix

    for (i=1;i<=NP;i++)   // loading data from zero besed `ze` to `a`
        for (j=1;j<=NP;j++) a[i][j]=z[i-1][j-1];

Complete test program is supplied below. It uses the eigen code from CACS:

/*******************************************************************************
Eigenvalue solvers, tred2 and tqli, from "Numerical Recipes in C" (Cambridge
Univ. Press) by W.H. Press, S.A. Teukolsky, W.T. Vetterling, and B.P. Flannery
*******************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define NR_END 1
#define SIGN(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a))

double **dmatrix(int nrl, int nrh, int ncl, int nch)
/* allocate a double matrix with subscript range m[nrl..nrh][ncl..nch] */
{
    int i,nrow=nrh-nrl+1,ncol=nch-ncl+1;
    double **m;
    /* allocate pointers to rows */
    m=(double **) malloc((size_t)((nrow+NR_END)*sizeof(double*)));
    m += NR_END;
    m -= nrl;
    /* allocate rows and set pointers to them */
    m[nrl]=(double *) malloc((size_t)((nrow*ncol+NR_END)*sizeof(double)));
    m[nrl] += NR_END;
    m[nrl] -= ncl;
    for(i=nrl+1;i<=nrh;i++) m[i]=m[i-1]+ncol;
    /* return pointer to array of pointers to rows */
    return m;
}

double *dvector(int nl, int nh)
/* allocate a double vector with subscript range v[nl..nh] */
{
    double *v;
    v=(double *)malloc((size_t) ((nh-nl+1+NR_END)*sizeof(double)));
    return v-nl+NR_END;
}

/******************************************************************************/
void tred2(double **a, int n, double d[], double e[])
/*******************************************************************************
Householder reduction of a real, symmetric matrix a[1..n][1..n]. 
On output, a is replaced by the orthogonal matrix Q effecting the
transformation. d[1..n] returns the diagonal elements of the tridiagonal matrix,
and e[1..n] the off-diagonal elements, with e[1]=0. Several statements, as noted
in comments, can be omitted if only eigenvalues are to be found, in which case a
contains no useful information on output. Otherwise they are to be included.
*******************************************************************************/
{
    int l,k,j,i;
    double scale,hh,h,g,f;

    for (i=n;i>=2;i--) {
        l=i-1;
        h=scale=0.0;
        if (l > 1) {
            for (k=1;k<=l;k++)
                scale += fabs(a[i][k]);
            if (scale == 0.0) /* Skip transformation. */
                e[i]=a[i][l];
            else {
                for (k=1;k<=l;k++) {
                    a[i][k] /= scale; /* Use scaled a's for transformation. */
                    h += a[i][k]*a[i][k]; /* Form sigma in h. */
                }
                f=a[i][l];
                g=(f >= 0.0 ? -sqrt(h) : sqrt(h));
                e[i]=scale*g;
                h -= f*g; /* Now h is equation (11.2.4). */
                a[i][l]=f-g; /* Store u in the ith row of a. */
                f=0.0;
                for (j=1;j<=l;j++) {
                    /* Next statement can be omitted if eigenvectors not wanted */
                    a[j][i]=a[i][j]/h; /* Store u/H in ith column of a. */
                    g=0.0; /* Form an element of A.u in g. */
                    for (k=1;k<=j;k++)
                        g += a[j][k]*a[i][k];
                    for (k=j+1;k<=l;k++)
                        g += a[k][j]*a[i][k];
                    e[j]=g/h; /* Form element of p in temporarily unused element of e. */
                    f += e[j]*a[i][j];
                }
                hh=f/(h+h); /* Form K, equation (11.2.11). */
                for (j=1;j<=l;j++) { /* Form q and store in e overwriting p. */
                    f=a[i][j];
                    e[j]=g=e[j]-hh*f;
                    for (k=1;k<=j;k++) /* Reduce a, equation (11.2.13). */
                        a[j][k] -= (f*e[k]+g*a[i][k]);
                }
            }
        } else
            e[i]=a[i][l];
        d[i]=h;
        }
        /* Next statement can be omitted if eigenvectors not wanted */
        d[1]=0.0;
        e[1]=0.0;
        /* Contents of this loop can be omitted if eigenvectors not
           wanted except for statement d[i]=a[i][i]; */
        for (i=1;i<=n;i++) { /* Begin accumulation of transformation matrices. */
            l=i-1;
        if (d[i]) { /* This block skipped when i=1. */
            for (j=1;j<=l;j++) {
                g=0.0;
                for (k=1;k<=l;k++) /* Use u and u/H stored in a to form P.Q. */
                    g += a[i][k]*a[k][j];
                for (k=1;k<=l;k++)
                    a[k][j] -= g*a[k][i];
            }
        }
        d[i]=a[i][i]; /* This statement remains. */
        a[i][i]=1.0; /* Reset row and column of a to identity matrix for next iteration. */
        for (j=1;j<=l;j++) a[j][i]=a[i][j]=0.0;
    }
}

/******************************************************************************/
void tqli(double d[], double e[], int n, double **z)
/*******************************************************************************
QL algorithm with implicit shifts, to determine the eigenvalues and eigenvectors
of a real, symmetric, tridiagonal matrix, or of a real, symmetric matrix
previously reduced by tred2 sec. 11.2. On input, d[1..n] contains the diagonal
elements of the tridiagonal matrix. On output, it returns the eigenvalues. The
vector e[1..n] inputs the subdiagonal elements of the tridiagonal matrix, with
e[1] arbitrary. On output e is destroyed. When finding only the eigenvalues,
several lines may be omitted, as noted in the comments. If the eigenvectors of
a tridiagonal matrix are desired, the matrix z[1..n][1..n] is input as the
identity matrix. If the eigenvectors of a matrix that has been reduced by tred2
are required, then z is input as the matrix output by tred2. In either case,
the kth column of z returns the normalized eigenvector corresponding to d[k].
*******************************************************************************/
{
    double pythag(double a, double b);
    int m,l,iter,i,k;
    double s,r,p,g,f,dd,c,b;

    for (i=2;i<=n;i++) e[i-1]=e[i]; /* Convenient to renumber the elements of e. */
    e[n]=0.0;
    for (l=1;l<=n;l++) {
        iter=0;
        do {
            for (m=l;m<=n-1;m++) { /* Look for a single small subdiagonal element to split the matrix. */
                dd=fabs(d[m])+fabs(d[m+1]);
                if ((double)(fabs(e[m])+dd) == dd) break;
            }
            if (m != l) {
                if (iter++ == 30) printf("Too many iterations in tqli");
                g=(d[l+1]-d[l])/(2.0*e[l]); /* Form shift. */
                r=pythag(g,1.0);
                g=d[m]-d[l]+e[l]/(g+SIGN(r,g)); /* This is dm - ks. */
                s=c=1.0;
                p=0.0;
                for (i=m-1;i>=l;i--) { /* A plane rotation as in the original QL, followed by Givens */
                    f=s*e[i];          /* rotations to restore tridiagonal form.                     */
                    b=c*e[i];
                    e[i+1]=(r=pythag(f,g));
                    if (r == 0.0) { /* Recover from underflow. */
                        d[i+1] -= p;
                        e[m]=0.0;
                        break;
                    }
                    s=f/r;
                    c=g/r;
                    g=d[i+1]-p;
                    r=(d[i]-g)*s+2.0*c*b;
                    d[i+1]=g+(p=s*r);
                    g=c*r-b;
                    /* Next loop can be omitted if eigenvectors not wanted */
                    for (k=1;k<=n;k++) { /* Form eigenvectors. */
                        f=z[k][i+1];
                        z[k][i+1]=s*z[k][i]+c*f;
                        z[k][i]=c*z[k][i]-s*f;
                    }
                }
                if (r == 0.0 && i >= l) continue;
                d[l] -= p;
                e[l]=g;
                e[m]=0.0;
            }
        } while (m != l);
    }
}

/******************************************************************************/
double pythag(double a, double b)
/*******************************************************************************
Computes (a2 + b2)1/2 without destructive underflow or overflow.
*******************************************************************************/
{
    double absa,absb;
    absa=fabs(a);
    absb=fabs(b);
    if (absa > absb) return absa*sqrt(1.0+(absb/absa)*(absb/absa));
    else return (absb == 0.0 ? 0.0 : absb*sqrt(1.0+(absa/absb)*(absa/absb)));
}


#define NP 3
#define TINY 1.0e-6

double sqrt(double x)
{
  union
  {
    int i;
    double x;
  } u;

  u.x = x;
  u.i = (1<<29) + (u.i >> 1) - (1<<22); 
  return u.x;
} 

int main()
{
    int i,j,k;
    double ze[NP][NP] = { {2, 0, 0}, {0, 4, 0}, {0, 0, 2} } ;
    double **a;
    double *d,*e,*f;

    d=dvector(1,NP);
    e=dvector(1,NP);
    f=dvector(1,NP);

    a=dmatrix(1,NP,1,NP);
    for (i=1;i<=NP;i++)
        for (j=1;j<=NP;j++) a[i][j]=ze[i-1][j-1];

    tred2(a,NP,d,e);
    tqli(d,e,NP,a);

    printf("\nEigenvectors for a real symmetric matrix:\n");

    for (i=1;i<=NP;i++) {
        for (j=1;j<=NP;j++) {
            f[j]=0.0;
            for (k=1;k<=NP;k++)
                f[j] += (ze[j-1][k-1]*a[k][i]);
        }

        printf("%s %3d %s %10.6f\n","\neigenvalue",i," =",d[i]);
        printf("%11s %14s %9s\n","vector","mtrx*vect.","ratio");

        for (j=1;j<=NP;j++) {
            if (fabs(a[j][i]) < TINY)
                printf("%12.6f %12.6f %12s\n",
                    a[j][i],f[j],"div. by 0");
            else
                printf("%12.6f %12.6f %12.6f\n",
                    a[j][i],f[j],f[j]/a[j][i]);
        }
    }

    //free_dmatrix(a,1,NP,1,NP);
    //free_dvector(f,1,NP);
    //free_dvector(e,1,NP);
    //free_dvector(d,1,NP);
    return 0;
}

Output:

Eigenvectors for a real symmetric matrix:

eigenvalue   1  =   2.000000
     vector     mtrx*vect.     ratio
    1.000000     2.000000     2.000000
    0.000000     0.000000    div. by 0
    0.000000     0.000000    div. by 0

eigenvalue   2  =   4.000000
     vector     mtrx*vect.     ratio
    0.000000     0.000000    div. by 0
    1.000000     4.000000     4.000000
    0.000000     0.000000    div. by 0

eigenvalue   3  =   2.000000
     vector     mtrx*vect.     ratio
    0.000000     0.000000    div. by 0
    0.000000     0.000000    div. by 0
    1.000000     2.000000     2.000000

I hope it finaly helps to clarify confusion regarding the data preparation for tqli.



来源:https://stackoverflow.com/questions/48945001/eigenvalue-calculation-using-tqli-algorithm-fails-with-segmentation-fault

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!