gonum.org/v1/gonum@v0.14.0/lapack/testlapack/dgeev.go (about)

     1  // Copyright ©2016 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package testlapack
     6  
     7  import (
     8  	"fmt"
     9  	"math"
    10  	"math/cmplx"
    11  	"strconv"
    12  	"testing"
    13  
    14  	"golang.org/x/exp/rand"
    15  
    16  	"gonum.org/v1/gonum/blas"
    17  	"gonum.org/v1/gonum/blas/blas64"
    18  	"gonum.org/v1/gonum/lapack"
    19  )
    20  
    21  type Dgeever interface {
    22  	Dgeev(jobvl lapack.LeftEVJob, jobvr lapack.RightEVJob, n int, a []float64, lda int,
    23  		wr, wi []float64, vl []float64, ldvl int, vr []float64, ldvr int, work []float64, lwork int) int
    24  }
    25  
    26  type dgeevTest struct {
    27  	a      blas64.General
    28  	evWant []complex128 // If nil, the eigenvalues are not known.
    29  	valTol float64      // Tolerance for eigenvalue checks.
    30  	vecTol float64      // Tolerance for eigenvector checks.
    31  }
    32  
    33  func DgeevTest(t *testing.T, impl Dgeever) {
    34  	rnd := rand.New(rand.NewSource(1))
    35  
    36  	for i, test := range []dgeevTest{
    37  		{
    38  			a:      A123{}.Matrix(),
    39  			evWant: A123{}.Eigenvalues(),
    40  		},
    41  
    42  		dgeevTestForAntisymRandom(10, rnd),
    43  		dgeevTestForAntisymRandom(11, rnd),
    44  		dgeevTestForAntisymRandom(50, rnd),
    45  		dgeevTestForAntisymRandom(51, rnd),
    46  		dgeevTestForAntisymRandom(100, rnd),
    47  		dgeevTestForAntisymRandom(101, rnd),
    48  
    49  		{
    50  			a:      Circulant(2).Matrix(),
    51  			evWant: Circulant(2).Eigenvalues(),
    52  		},
    53  		{
    54  			a:      Circulant(3).Matrix(),
    55  			evWant: Circulant(3).Eigenvalues(),
    56  		},
    57  		{
    58  			a:      Circulant(4).Matrix(),
    59  			evWant: Circulant(4).Eigenvalues(),
    60  		},
    61  		{
    62  			a:      Circulant(5).Matrix(),
    63  			evWant: Circulant(5).Eigenvalues(),
    64  		},
    65  		{
    66  			a:      Circulant(10).Matrix(),
    67  			evWant: Circulant(10).Eigenvalues(),
    68  		},
    69  		{
    70  			a:      Circulant(15).Matrix(),
    71  			evWant: Circulant(15).Eigenvalues(),
    72  			valTol: 1e-12,
    73  		},
    74  		{
    75  			a:      Circulant(30).Matrix(),
    76  			evWant: Circulant(30).Eigenvalues(),
    77  			valTol: 1e-11,
    78  		},
    79  		{
    80  			a:      Circulant(50).Matrix(),
    81  			evWant: Circulant(50).Eigenvalues(),
    82  			valTol: 1e-11,
    83  		},
    84  		{
    85  			a:      Circulant(101).Matrix(),
    86  			evWant: Circulant(101).Eigenvalues(),
    87  			valTol: 1e-10,
    88  		},
    89  		{
    90  			a:      Circulant(150).Matrix(),
    91  			evWant: Circulant(150).Eigenvalues(),
    92  			valTol: 1e-9,
    93  		},
    94  
    95  		{
    96  			a:      Clement(2).Matrix(),
    97  			evWant: Clement(2).Eigenvalues(),
    98  		},
    99  		{
   100  			a:      Clement(3).Matrix(),
   101  			evWant: Clement(3).Eigenvalues(),
   102  		},
   103  		{
   104  			a:      Clement(4).Matrix(),
   105  			evWant: Clement(4).Eigenvalues(),
   106  		},
   107  		{
   108  			a:      Clement(5).Matrix(),
   109  			evWant: Clement(5).Eigenvalues(),
   110  		},
   111  		{
   112  			a:      Clement(10).Matrix(),
   113  			evWant: Clement(10).Eigenvalues(),
   114  		},
   115  		{
   116  			a:      Clement(15).Matrix(),
   117  			evWant: Clement(15).Eigenvalues(),
   118  		},
   119  		{
   120  			a:      Clement(30).Matrix(),
   121  			evWant: Clement(30).Eigenvalues(),
   122  			valTol: 1e-11,
   123  		},
   124  		{
   125  			a:      Clement(50).Matrix(),
   126  			evWant: Clement(50).Eigenvalues(),
   127  			valTol: 1e-8,
   128  		},
   129  
   130  		{
   131  			a:      Creation(2).Matrix(),
   132  			evWant: Creation(2).Eigenvalues(),
   133  		},
   134  		{
   135  			a:      Creation(3).Matrix(),
   136  			evWant: Creation(3).Eigenvalues(),
   137  		},
   138  		{
   139  			a:      Creation(4).Matrix(),
   140  			evWant: Creation(4).Eigenvalues(),
   141  		},
   142  		{
   143  			a:      Creation(5).Matrix(),
   144  			evWant: Creation(5).Eigenvalues(),
   145  		},
   146  		{
   147  			a:      Creation(10).Matrix(),
   148  			evWant: Creation(10).Eigenvalues(),
   149  		},
   150  		{
   151  			a:      Creation(15).Matrix(),
   152  			evWant: Creation(15).Eigenvalues(),
   153  		},
   154  		{
   155  			a:      Creation(30).Matrix(),
   156  			evWant: Creation(30).Eigenvalues(),
   157  		},
   158  		{
   159  			a:      Creation(50).Matrix(),
   160  			evWant: Creation(50).Eigenvalues(),
   161  		},
   162  		{
   163  			a:      Creation(101).Matrix(),
   164  			evWant: Creation(101).Eigenvalues(),
   165  		},
   166  		{
   167  			a:      Creation(150).Matrix(),
   168  			evWant: Creation(150).Eigenvalues(),
   169  		},
   170  
   171  		{
   172  			a:      Diagonal(0).Matrix(),
   173  			evWant: Diagonal(0).Eigenvalues(),
   174  		},
   175  		{
   176  			a:      Diagonal(10).Matrix(),
   177  			evWant: Diagonal(10).Eigenvalues(),
   178  		},
   179  		{
   180  			a:      Diagonal(50).Matrix(),
   181  			evWant: Diagonal(50).Eigenvalues(),
   182  		},
   183  		{
   184  			a:      Diagonal(151).Matrix(),
   185  			evWant: Diagonal(151).Eigenvalues(),
   186  		},
   187  
   188  		{
   189  			a:      Downshift(2).Matrix(),
   190  			evWant: Downshift(2).Eigenvalues(),
   191  		},
   192  		{
   193  			a:      Downshift(3).Matrix(),
   194  			evWant: Downshift(3).Eigenvalues(),
   195  		},
   196  		{
   197  			a:      Downshift(4).Matrix(),
   198  			evWant: Downshift(4).Eigenvalues(),
   199  		},
   200  		{
   201  			a:      Downshift(5).Matrix(),
   202  			evWant: Downshift(5).Eigenvalues(),
   203  		},
   204  		{
   205  			a:      Downshift(10).Matrix(),
   206  			evWant: Downshift(10).Eigenvalues(),
   207  		},
   208  		{
   209  			a:      Downshift(15).Matrix(),
   210  			evWant: Downshift(15).Eigenvalues(),
   211  		},
   212  		{
   213  			a:      Downshift(30).Matrix(),
   214  			evWant: Downshift(30).Eigenvalues(),
   215  		},
   216  		{
   217  			a:      Downshift(50).Matrix(),
   218  			evWant: Downshift(50).Eigenvalues(),
   219  		},
   220  		{
   221  			a:      Downshift(101).Matrix(),
   222  			evWant: Downshift(101).Eigenvalues(),
   223  		},
   224  		{
   225  			a:      Downshift(150).Matrix(),
   226  			evWant: Downshift(150).Eigenvalues(),
   227  		},
   228  
   229  		{
   230  			a:      Fibonacci(2).Matrix(),
   231  			evWant: Fibonacci(2).Eigenvalues(),
   232  		},
   233  		{
   234  			a:      Fibonacci(3).Matrix(),
   235  			evWant: Fibonacci(3).Eigenvalues(),
   236  		},
   237  		{
   238  			a:      Fibonacci(4).Matrix(),
   239  			evWant: Fibonacci(4).Eigenvalues(),
   240  		},
   241  		{
   242  			a:      Fibonacci(5).Matrix(),
   243  			evWant: Fibonacci(5).Eigenvalues(),
   244  		},
   245  		{
   246  			a:      Fibonacci(10).Matrix(),
   247  			evWant: Fibonacci(10).Eigenvalues(),
   248  		},
   249  		{
   250  			a:      Fibonacci(15).Matrix(),
   251  			evWant: Fibonacci(15).Eigenvalues(),
   252  		},
   253  		{
   254  			a:      Fibonacci(30).Matrix(),
   255  			evWant: Fibonacci(30).Eigenvalues(),
   256  		},
   257  		{
   258  			a:      Fibonacci(50).Matrix(),
   259  			evWant: Fibonacci(50).Eigenvalues(),
   260  		},
   261  		{
   262  			a:      Fibonacci(101).Matrix(),
   263  			evWant: Fibonacci(101).Eigenvalues(),
   264  		},
   265  		{
   266  			a:      Fibonacci(150).Matrix(),
   267  			evWant: Fibonacci(150).Eigenvalues(),
   268  		},
   269  
   270  		{
   271  			a:      Gear(2).Matrix(),
   272  			evWant: Gear(2).Eigenvalues(),
   273  		},
   274  		{
   275  			a:      Gear(3).Matrix(),
   276  			evWant: Gear(3).Eigenvalues(),
   277  		},
   278  		{
   279  			a:      Gear(4).Matrix(),
   280  			evWant: Gear(4).Eigenvalues(),
   281  			valTol: 1e-7,
   282  			vecTol: 1e-8,
   283  		},
   284  		{
   285  			a:      Gear(5).Matrix(),
   286  			evWant: Gear(5).Eigenvalues(),
   287  		},
   288  		{
   289  			a:      Gear(10).Matrix(),
   290  			evWant: Gear(10).Eigenvalues(),
   291  			valTol: 1e-8,
   292  		},
   293  		{
   294  			a:      Gear(15).Matrix(),
   295  			evWant: Gear(15).Eigenvalues(),
   296  		},
   297  		{
   298  			a:      Gear(30).Matrix(),
   299  			evWant: Gear(30).Eigenvalues(),
   300  			valTol: 1e-8,
   301  		},
   302  		{
   303  			a:      Gear(50).Matrix(),
   304  			evWant: Gear(50).Eigenvalues(),
   305  			valTol: 1e-8,
   306  		},
   307  		{
   308  			a:      Gear(101).Matrix(),
   309  			evWant: Gear(101).Eigenvalues(),
   310  		},
   311  		{
   312  			a:      Gear(150).Matrix(),
   313  			evWant: Gear(150).Eigenvalues(),
   314  			valTol: 1e-8,
   315  		},
   316  
   317  		{
   318  			a:      Grcar{N: 10, K: 3}.Matrix(),
   319  			evWant: Grcar{N: 10, K: 3}.Eigenvalues(),
   320  		},
   321  		{
   322  			a:      Grcar{N: 10, K: 7}.Matrix(),
   323  			evWant: Grcar{N: 10, K: 7}.Eigenvalues(),
   324  		},
   325  		{
   326  			a:      Grcar{N: 11, K: 7}.Matrix(),
   327  			evWant: Grcar{N: 11, K: 7}.Eigenvalues(),
   328  		},
   329  		{
   330  			a:      Grcar{N: 50, K: 3}.Matrix(),
   331  			evWant: Grcar{N: 50, K: 3}.Eigenvalues(),
   332  		},
   333  		{
   334  			a:      Grcar{N: 51, K: 3}.Matrix(),
   335  			evWant: Grcar{N: 51, K: 3}.Eigenvalues(),
   336  		},
   337  		{
   338  			a:      Grcar{N: 50, K: 10}.Matrix(),
   339  			evWant: Grcar{N: 50, K: 10}.Eigenvalues(),
   340  		},
   341  		{
   342  			a:      Grcar{N: 51, K: 10}.Matrix(),
   343  			evWant: Grcar{N: 51, K: 10}.Eigenvalues(),
   344  		},
   345  		{
   346  			a:      Grcar{N: 50, K: 30}.Matrix(),
   347  			evWant: Grcar{N: 50, K: 30}.Eigenvalues(),
   348  		},
   349  		{
   350  			a:      Grcar{N: 150, K: 2}.Matrix(),
   351  			evWant: Grcar{N: 150, K: 2}.Eigenvalues(),
   352  		},
   353  		{
   354  			a:      Grcar{N: 150, K: 148}.Matrix(),
   355  			evWant: Grcar{N: 150, K: 148}.Eigenvalues(),
   356  		},
   357  
   358  		{
   359  			a:      Hanowa{N: 6, Alpha: 17}.Matrix(),
   360  			evWant: Hanowa{N: 6, Alpha: 17}.Eigenvalues(),
   361  		},
   362  		{
   363  			a:      Hanowa{N: 50, Alpha: -1}.Matrix(),
   364  			evWant: Hanowa{N: 50, Alpha: -1}.Eigenvalues(),
   365  		},
   366  		{
   367  			a:      Hanowa{N: 100, Alpha: -1}.Matrix(),
   368  			evWant: Hanowa{N: 100, Alpha: -1}.Eigenvalues(),
   369  		},
   370  
   371  		{
   372  			a:      Lesp(2).Matrix(),
   373  			evWant: Lesp(2).Eigenvalues(),
   374  		},
   375  		{
   376  			a:      Lesp(3).Matrix(),
   377  			evWant: Lesp(3).Eigenvalues(),
   378  		},
   379  		{
   380  			a:      Lesp(4).Matrix(),
   381  			evWant: Lesp(4).Eigenvalues(),
   382  		},
   383  		{
   384  			a:      Lesp(5).Matrix(),
   385  			evWant: Lesp(5).Eigenvalues(),
   386  		},
   387  		{
   388  			a:      Lesp(10).Matrix(),
   389  			evWant: Lesp(10).Eigenvalues(),
   390  		},
   391  		{
   392  			a:      Lesp(15).Matrix(),
   393  			evWant: Lesp(15).Eigenvalues(),
   394  		},
   395  		{
   396  			a:      Lesp(30).Matrix(),
   397  			evWant: Lesp(30).Eigenvalues(),
   398  		},
   399  		{
   400  			a:      Lesp(50).Matrix(),
   401  			evWant: Lesp(50).Eigenvalues(),
   402  			valTol: 1e-12,
   403  		},
   404  		{
   405  			a:      Lesp(101).Matrix(),
   406  			evWant: Lesp(101).Eigenvalues(),
   407  			valTol: 1e-12,
   408  		},
   409  		{
   410  			a:      Lesp(150).Matrix(),
   411  			evWant: Lesp(150).Eigenvalues(),
   412  			valTol: 1e-12,
   413  		},
   414  
   415  		{
   416  			a:      Rutis{}.Matrix(),
   417  			evWant: Rutis{}.Eigenvalues(),
   418  		},
   419  
   420  		{
   421  			a:      Tris{N: 74, X: 1, Y: -2, Z: 1}.Matrix(),
   422  			evWant: Tris{N: 74, X: 1, Y: -2, Z: 1}.Eigenvalues(),
   423  		},
   424  		{
   425  			a:      Tris{N: 74, X: 1, Y: 2, Z: -3}.Matrix(),
   426  			evWant: Tris{N: 74, X: 1, Y: 2, Z: -3}.Eigenvalues(),
   427  		},
   428  		{
   429  			a:      Tris{N: 75, X: 1, Y: 2, Z: -3}.Matrix(),
   430  			evWant: Tris{N: 75, X: 1, Y: 2, Z: -3}.Eigenvalues(),
   431  		},
   432  
   433  		{
   434  			a:      Wilk4{}.Matrix(),
   435  			evWant: Wilk4{}.Eigenvalues(),
   436  		},
   437  		{
   438  			a:      Wilk12{}.Matrix(),
   439  			evWant: Wilk12{}.Eigenvalues(),
   440  			valTol: 1e-7,
   441  		},
   442  		{
   443  			a:      Wilk20(0).Matrix(),
   444  			evWant: Wilk20(0).Eigenvalues(),
   445  		},
   446  		{
   447  			a:      Wilk20(1e-10).Matrix(),
   448  			evWant: Wilk20(1e-10).Eigenvalues(),
   449  			valTol: 1e-12,
   450  		},
   451  
   452  		{
   453  			a:      Zero(1).Matrix(),
   454  			evWant: Zero(1).Eigenvalues(),
   455  		},
   456  		{
   457  			a:      Zero(10).Matrix(),
   458  			evWant: Zero(10).Eigenvalues(),
   459  		},
   460  		{
   461  			a:      Zero(50).Matrix(),
   462  			evWant: Zero(50).Eigenvalues(),
   463  		},
   464  		{
   465  			a:      Zero(100).Matrix(),
   466  			evWant: Zero(100).Eigenvalues(),
   467  		},
   468  	} {
   469  		for _, jobvl := range []lapack.LeftEVJob{lapack.LeftEVCompute, lapack.LeftEVNone} {
   470  			for _, jobvr := range []lapack.RightEVJob{lapack.RightEVCompute, lapack.RightEVNone} {
   471  				for _, extra := range []int{0, 11} {
   472  					for _, wl := range []worklen{minimumWork, mediumWork, optimumWork} {
   473  						testDgeev(t, impl, strconv.Itoa(i), test, jobvl, jobvr, extra, wl)
   474  					}
   475  				}
   476  			}
   477  		}
   478  	}
   479  
   480  	for _, n := range []int{2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 20, 50, 51, 100, 101} {
   481  		for _, jobvl := range []lapack.LeftEVJob{lapack.LeftEVCompute, lapack.LeftEVNone} {
   482  			for _, jobvr := range []lapack.RightEVJob{lapack.RightEVCompute, lapack.RightEVNone} {
   483  				for cas := 0; cas < 10; cas++ {
   484  					// Create a block diagonal matrix with
   485  					// random eigenvalues of random multiplicity.
   486  					ev := make([]complex128, n)
   487  					tmat := zeros(n, n, n)
   488  					for i := 0; i < n; {
   489  						re := rnd.NormFloat64()
   490  						if i == n-1 || rnd.Float64() < 0.5 {
   491  							// Real eigenvalue.
   492  							nb := rnd.Intn(min(4, n-i)) + 1
   493  							for k := 0; k < nb; k++ {
   494  								tmat.Data[i*tmat.Stride+i] = re
   495  								ev[i] = complex(re, 0)
   496  								i++
   497  							}
   498  							continue
   499  						}
   500  						// Complex eigenvalue.
   501  						im := rnd.NormFloat64()
   502  						nb := rnd.Intn(min(4, (n-i)/2)) + 1
   503  						for k := 0; k < nb; k++ {
   504  							// 2×2 block for the complex eigenvalue.
   505  							tmat.Data[i*tmat.Stride+i] = re
   506  							tmat.Data[(i+1)*tmat.Stride+i+1] = re
   507  							tmat.Data[(i+1)*tmat.Stride+i] = -im
   508  							tmat.Data[i*tmat.Stride+i+1] = im
   509  							ev[i] = complex(re, im)
   510  							ev[i+1] = complex(re, -im)
   511  							i += 2
   512  						}
   513  					}
   514  
   515  					// Compute A = Q T Qᵀ where Q is an
   516  					// orthogonal matrix.
   517  					q := randomOrthogonal(n, rnd)
   518  					tq := zeros(n, n, n)
   519  					blas64.Gemm(blas.NoTrans, blas.Trans, 1, tmat, q, 0, tq)
   520  					a := zeros(n, n, n)
   521  					blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, q, tq, 0, a)
   522  
   523  					test := dgeevTest{
   524  						a:      a,
   525  						evWant: ev,
   526  						vecTol: 1e-7,
   527  					}
   528  					testDgeev(t, impl, "random", test, jobvl, jobvr, 0, optimumWork)
   529  				}
   530  			}
   531  		}
   532  	}
   533  }
   534  
   535  func testDgeev(t *testing.T, impl Dgeever, tc string, test dgeevTest, jobvl lapack.LeftEVJob, jobvr lapack.RightEVJob, extra int, wl worklen) {
   536  	const defaultTol = 1e-13
   537  	valTol := test.valTol
   538  	if valTol == 0 {
   539  		valTol = defaultTol
   540  	}
   541  	vecTol := test.vecTol
   542  	if vecTol == 0 {
   543  		vecTol = defaultTol
   544  	}
   545  
   546  	a := cloneGeneral(test.a)
   547  	n := a.Rows
   548  
   549  	var vl blas64.General
   550  	if jobvl == lapack.LeftEVCompute {
   551  		vl = nanGeneral(n, n, n)
   552  	} else {
   553  		vl.Stride = 1
   554  	}
   555  
   556  	var vr blas64.General
   557  	if jobvr == lapack.RightEVCompute {
   558  		vr = nanGeneral(n, n, n)
   559  	} else {
   560  		vr.Stride = 1
   561  	}
   562  
   563  	wr := make([]float64, n)
   564  	wi := make([]float64, n)
   565  
   566  	var lwork int
   567  	switch wl {
   568  	case minimumWork:
   569  		if jobvl == lapack.LeftEVCompute || jobvr == lapack.RightEVCompute {
   570  			lwork = max(1, 4*n)
   571  		} else {
   572  			lwork = max(1, 3*n)
   573  		}
   574  	case mediumWork:
   575  		work := make([]float64, 1)
   576  		impl.Dgeev(jobvl, jobvr, n, a.Data, a.Stride, wr, wi, vl.Data, vl.Stride, vr.Data, vr.Stride, work, -1)
   577  		if jobvl == lapack.LeftEVCompute || jobvr == lapack.RightEVCompute {
   578  			lwork = (int(work[0]) + 4*n) / 2
   579  		} else {
   580  			lwork = (int(work[0]) + 3*n) / 2
   581  		}
   582  		lwork = max(1, lwork)
   583  	case optimumWork:
   584  		work := make([]float64, 1)
   585  		impl.Dgeev(jobvl, jobvr, n, a.Data, a.Stride, wr, wi, vl.Data, vl.Stride, vr.Data, vr.Stride, work, -1)
   586  		lwork = int(work[0])
   587  	}
   588  	work := make([]float64, lwork)
   589  
   590  	first := impl.Dgeev(jobvl, jobvr, n, a.Data, a.Stride, wr, wi,
   591  		vl.Data, vl.Stride, vr.Data, vr.Stride, work, len(work))
   592  
   593  	prefix := fmt.Sprintf("Case #%v: n=%v, jobvl=%c, jobvr=%c, extra=%v, work=%v",
   594  		tc, n, jobvl, jobvr, extra, wl)
   595  
   596  	if !generalOutsideAllNaN(vl) {
   597  		t.Errorf("%v: out-of-range write to VL", prefix)
   598  	}
   599  	if !generalOutsideAllNaN(vr) {
   600  		t.Errorf("%v: out-of-range write to VR", prefix)
   601  	}
   602  
   603  	if first > 0 {
   604  		t.Logf("%v: all eigenvalues haven't been computed, first=%v", prefix, first)
   605  	}
   606  
   607  	// Check that conjugate pair eigenvalues are ordered correctly.
   608  	for i := first; i < n; {
   609  		if wi[i] == 0 {
   610  			i++
   611  			continue
   612  		}
   613  		if wr[i] != wr[i+1] {
   614  			t.Errorf("%v: real parts of %vth conjugate pair not equal", prefix, i)
   615  		}
   616  		if wi[i] < 0 || wi[i+1] >= 0 {
   617  			t.Errorf("%v: unexpected ordering of %vth conjugate pair", prefix, i)
   618  		}
   619  		i += 2
   620  	}
   621  
   622  	// Check the computed eigenvalues against provided known eigenvalues.
   623  	if test.evWant != nil {
   624  		used := make([]bool, n)
   625  		for i := first; i < n; i++ {
   626  			evGot := complex(wr[i], wi[i])
   627  			idx := -1
   628  			for k, evWant := range test.evWant {
   629  				if !used[k] && cmplx.Abs(evWant-evGot) < valTol {
   630  					idx = k
   631  					used[k] = true
   632  					break
   633  				}
   634  			}
   635  			if idx == -1 {
   636  				t.Errorf("%v: unexpected eigenvalue %v", prefix, evGot)
   637  			}
   638  		}
   639  	}
   640  
   641  	if first > 0 || (jobvl == lapack.LeftEVNone && jobvr == lapack.RightEVNone) {
   642  		// No eigenvectors have been computed.
   643  		return
   644  	}
   645  
   646  	// Check that the columns of VL and VR are eigenvectors that:
   647  	//  - correspond to the computed eigenvalues
   648  	//  - have Euclidean norm equal to 1
   649  	//  - have the largest component real
   650  	bi := blas64.Implementation()
   651  	if jobvr == lapack.RightEVCompute {
   652  		resid := residualRightEV(test.a, vr, wr, wi)
   653  		if resid > vecTol {
   654  			t.Errorf("%v: unexpected right eigenvectors; residual=%v, want<=%v", prefix, resid, vecTol)
   655  		}
   656  
   657  		for j := 0; j < n; j++ {
   658  			nrm := 1.0
   659  			if wi[j] == 0 {
   660  				nrm = bi.Dnrm2(n, vr.Data[j:], vr.Stride)
   661  			} else if wi[j] > 0 {
   662  				nrm = math.Hypot(bi.Dnrm2(n, vr.Data[j:], vr.Stride), bi.Dnrm2(n, vr.Data[j+1:], vr.Stride))
   663  			}
   664  			diff := math.Abs(nrm - 1)
   665  			if diff > defaultTol {
   666  				t.Errorf("%v: unexpected Euclidean norm of right eigenvector; |VR[%v]-1|=%v, want<=%v",
   667  					prefix, j, diff, defaultTol)
   668  			}
   669  
   670  			if wi[j] > 0 {
   671  				var vmax float64  // Largest component in the column
   672  				var vrmax float64 // Largest real component in the column
   673  				for i := 0; i < n; i++ {
   674  					vtest := math.Hypot(vr.Data[i*vr.Stride+j], vr.Data[i*vr.Stride+j+1])
   675  					vmax = math.Max(vmax, vtest)
   676  					if vr.Data[i*vr.Stride+j+1] == 0 {
   677  						vrmax = math.Max(vrmax, math.Abs(vr.Data[i*vr.Stride+j]))
   678  					}
   679  				}
   680  				if vrmax/vmax < 1-defaultTol {
   681  					t.Errorf("%v: largest component of %vth right eigenvector is not real", prefix, j)
   682  				}
   683  			}
   684  		}
   685  	}
   686  	if jobvl == lapack.LeftEVCompute {
   687  		resid := residualLeftEV(test.a, vl, wr, wi)
   688  		if resid > vecTol {
   689  			t.Errorf("%v: unexpected left eigenvectors; residual=%v, want<=%v", prefix, resid, vecTol)
   690  		}
   691  
   692  		for j := 0; j < n; j++ {
   693  			nrm := 1.0
   694  			if wi[j] == 0 {
   695  				nrm = bi.Dnrm2(n, vl.Data[j:], vl.Stride)
   696  			} else if wi[j] > 0 {
   697  				nrm = math.Hypot(bi.Dnrm2(n, vl.Data[j:], vl.Stride), bi.Dnrm2(n, vl.Data[j+1:], vl.Stride))
   698  			}
   699  			diff := math.Abs(nrm - 1)
   700  			if diff > defaultTol {
   701  				t.Errorf("%v: unexpected Euclidean norm of left eigenvector; |VL[%v]-1|=%v, want<=%v",
   702  					prefix, j, diff, defaultTol)
   703  			}
   704  
   705  			if wi[j] > 0 {
   706  				var vmax float64  // Largest component in the column
   707  				var vrmax float64 // Largest real component in the column
   708  				for i := 0; i < n; i++ {
   709  					vtest := math.Hypot(vl.Data[i*vl.Stride+j], vl.Data[i*vl.Stride+j+1])
   710  					vmax = math.Max(vmax, vtest)
   711  					if vl.Data[i*vl.Stride+j+1] == 0 {
   712  						vrmax = math.Max(vrmax, math.Abs(vl.Data[i*vl.Stride+j]))
   713  					}
   714  				}
   715  				if vrmax/vmax < 1-defaultTol {
   716  					t.Errorf("%v: largest component of %vth left eigenvector is not real", prefix, j)
   717  				}
   718  			}
   719  		}
   720  	}
   721  }
   722  
   723  func dgeevTestForAntisymRandom(n int, rnd *rand.Rand) dgeevTest {
   724  	a := NewAntisymRandom(n, rnd)
   725  	return dgeevTest{
   726  		a:      a.Matrix(),
   727  		evWant: a.Eigenvalues(),
   728  	}
   729  }
   730  
   731  // residualRightEV returns the residual
   732  //
   733  //	| A E - E W|_1 / ( |A|_1 |E|_1 )
   734  //
   735  // where the columns of E contain the right eigenvectors of A and W is a block diagonal matrix with
   736  // a 1×1 block for each real eigenvalue and a 2×2 block for each complex conjugate pair.
   737  func residualRightEV(a, e blas64.General, wr, wi []float64) float64 {
   738  	// The implementation follows DGET22 routine from the Reference LAPACK's
   739  	// testing suite.
   740  
   741  	n := a.Rows
   742  	if n == 0 {
   743  		return 0
   744  	}
   745  
   746  	bi := blas64.Implementation()
   747  	ldr := n
   748  	r := make([]float64, n*ldr)
   749  	var (
   750  		wmat  [4]float64
   751  		ipair int
   752  	)
   753  	for j := 0; j < n; j++ {
   754  		if ipair == 0 && wi[j] != 0 {
   755  			ipair = 1
   756  		}
   757  		switch ipair {
   758  		case 0:
   759  			// Real eigenvalue, multiply j-th column of E with it.
   760  			bi.Daxpy(n, wr[j], e.Data[j:], e.Stride, r[j:], ldr)
   761  		case 1:
   762  			// First of complex conjugate pair of eigenvalues
   763  			wmat[0], wmat[1] = wr[j], wi[j]
   764  			wmat[2], wmat[3] = -wi[j], wr[j]
   765  			bi.Dgemm(blas.NoTrans, blas.NoTrans, n, 2, 2, 1, e.Data[j:], e.Stride, wmat[:], 2, 0, r[j:], ldr)
   766  			ipair = 2
   767  		case 2:
   768  			// Second of complex conjugate pair of eigenvalues
   769  			ipair = 0
   770  		}
   771  	}
   772  	bi.Dgemm(blas.NoTrans, blas.NoTrans, n, n, n, 1, a.Data, a.Stride, e.Data, e.Stride, -1, r, ldr)
   773  
   774  	const eps = dlamchE
   775  	anorm := math.Max(dlange(lapack.MaxColumnSum, n, n, a.Data, a.Stride), safmin)
   776  	enorm := math.Max(dlange(lapack.MaxColumnSum, n, n, e.Data, e.Stride), eps)
   777  	errnorm := dlange(lapack.MaxColumnSum, n, n, r, ldr) / enorm
   778  	if anorm > errnorm {
   779  		return errnorm / anorm
   780  	}
   781  	if anorm < 1 {
   782  		return math.Min(errnorm, anorm) / anorm
   783  	}
   784  	return math.Min(errnorm/anorm, 1)
   785  }
   786  
   787  // residualLeftEV returns the residual
   788  //
   789  //	| Aᵀ E - E Wᵀ|_1 / ( |Aᵀ|_1 |E|_1 )
   790  //
   791  // where the columns of E contain the left eigenvectors of A and W is a block diagonal matrix with
   792  // a 1×1 block for each real eigenvalue and a 2×2 block for each complex conjugate pair.
   793  func residualLeftEV(a, e blas64.General, wr, wi []float64) float64 {
   794  	// The implementation follows DGET22 routine from the Reference LAPACK's
   795  	// testing suite.
   796  
   797  	n := a.Rows
   798  	if n == 0 {
   799  		return 0
   800  	}
   801  
   802  	bi := blas64.Implementation()
   803  	ldr := n
   804  	r := make([]float64, n*ldr)
   805  	var (
   806  		wmat  [4]float64
   807  		ipair int
   808  	)
   809  	for j := 0; j < n; j++ {
   810  		if ipair == 0 && wi[j] != 0 {
   811  			ipair = 1
   812  		}
   813  		switch ipair {
   814  		case 0:
   815  			// Real eigenvalue, multiply j-th column of E with it.
   816  			bi.Daxpy(n, wr[j], e.Data[j:], e.Stride, r[j:], ldr)
   817  		case 1:
   818  			// First of complex conjugate pair of eigenvalues
   819  			wmat[0], wmat[1] = wr[j], wi[j]
   820  			wmat[2], wmat[3] = -wi[j], wr[j]
   821  			bi.Dgemm(blas.NoTrans, blas.Trans, n, 2, 2, 1, e.Data[j:], e.Stride, wmat[:], 2, 0, r[j:], ldr)
   822  			ipair = 2
   823  		case 2:
   824  			// Second of complex conjugate pair of eigenvalues
   825  			ipair = 0
   826  		}
   827  	}
   828  	bi.Dgemm(blas.Trans, blas.NoTrans, n, n, n, 1, a.Data, a.Stride, e.Data, e.Stride, -1, r, ldr)
   829  
   830  	const eps = dlamchE
   831  	anorm := math.Max(dlange(lapack.MaxRowSum, n, n, a.Data, a.Stride), safmin)
   832  	enorm := math.Max(dlange(lapack.MaxColumnSum, n, n, e.Data, e.Stride), eps)
   833  	errnorm := dlange(lapack.MaxColumnSum, n, n, r, ldr) / enorm
   834  	if anorm > errnorm {
   835  		return errnorm / anorm
   836  	}
   837  	if anorm < 1 {
   838  		return math.Min(errnorm, anorm) / anorm
   839  	}
   840  	return math.Min(errnorm/anorm, 1)
   841  }