gonum.org/v1/gonum@v0.14.0/mat/eigen_test.go (about)

     1  // Copyright ©2013 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 mat
     6  
     7  import (
     8  	"sort"
     9  	"testing"
    10  
    11  	"golang.org/x/exp/rand"
    12  
    13  	"gonum.org/v1/gonum/floats"
    14  )
    15  
    16  func TestEigen(t *testing.T) {
    17  	t.Parallel()
    18  	for i, test := range []struct {
    19  		a *Dense
    20  
    21  		values []complex128
    22  		left   *CDense
    23  		right  *CDense
    24  	}{
    25  		{
    26  			a: NewDense(3, 3, []float64{
    27  				1, 0, 0,
    28  				0, 1, 0,
    29  				0, 0, 1,
    30  			}),
    31  			values: []complex128{1, 1, 1},
    32  			left: NewCDense(3, 3, []complex128{
    33  				1, 0, 0,
    34  				0, 1, 0,
    35  				0, 0, 1,
    36  			}),
    37  			right: NewCDense(3, 3, []complex128{
    38  				1, 0, 0,
    39  				0, 1, 0,
    40  				0, 0, 1,
    41  			}),
    42  		},
    43  		{
    44  			// Values compared with numpy.
    45  			a: NewDense(4, 4, []float64{
    46  				0.9025, 0.025, 0.475, 0.0475,
    47  				0.0475, 0.475, 0.475, 0.0025,
    48  				0.0475, 0.025, 0.025, 0.9025,
    49  				0.0025, 0.475, 0.025, 0.0475,
    50  			}),
    51  			values: []complex128{1, 0.7300317046114154, -0.1400158523057075 + 0.452854925738716i, -0.1400158523057075 - 0.452854925738716i},
    52  			left: NewCDense(4, 4, []complex128{
    53  				0.5, -0.3135167160788313, -0.02058121780136903 + 0.004580939300127051i, -0.02058121780136903 - 0.004580939300127051i,
    54  				0.5, 0.7842199280224781, 0.37551026954193356 - 0.2924634904103879i, 0.37551026954193356 + 0.2924634904103879i,
    55  				0.5, 0.33202200780783525, 0.16052616322784943 + 0.3881393645202527i, 0.16052616322784943 - 0.3881393645202527i,
    56  				0.5, 0.42008065840123954, -0.7723935249234155, -0.7723935249234155,
    57  			}),
    58  			right: NewCDense(4, 4, []complex128{
    59  				0.9476399565969628, -0.8637347682162745, -0.2688989440320280 - 0.1282234938321029i, -0.2688989440320280 + 0.1282234938321029i,
    60  				0.2394935907064427, 0.3457075153704627, -0.3621360383713332 - 0.2583198964498771i, -0.3621360383713332 + 0.2583198964498771i,
    61  				0.1692743801716332, 0.2706851011641580, 0.7426369401030960, 0.7426369401030960,
    62  				0.1263626404003607, 0.2473421516816520, -0.1116019576997347 + 0.3865433902819795i, -0.1116019576997347 - 0.3865433902819795i,
    63  			}),
    64  		},
    65  	} {
    66  		var e1, e2, e3, e4 Eigen
    67  		ok := e1.Factorize(test.a, EigenBoth)
    68  		if !ok {
    69  			panic("bad factorization")
    70  		}
    71  		e2.Factorize(test.a, EigenRight)
    72  		e3.Factorize(test.a, EigenLeft)
    73  		e4.Factorize(test.a, EigenNone)
    74  
    75  		v1 := e1.Values(nil)
    76  		if !cmplxEqualTol(v1, test.values, 1e-14) {
    77  			t.Errorf("eigenvalue mismatch. Case %v", i)
    78  		}
    79  		var left CDense
    80  		e1.LeftVectorsTo(&left)
    81  		if !CEqualApprox(&left, test.left, 1e-14) {
    82  			t.Errorf("left eigenvector mismatch. Case %v", i)
    83  		}
    84  		var right CDense
    85  		e1.VectorsTo(&right)
    86  		if !CEqualApprox(&right, test.right, 1e-14) {
    87  			t.Errorf("right eigenvector mismatch. Case %v", i)
    88  		}
    89  
    90  		// Check that the eigenvectors and values are the same in all combinations.
    91  		if !cmplxEqual(v1, e2.Values(nil)) {
    92  			t.Errorf("eigenvector mismatch. Case %v", i)
    93  		}
    94  		if !cmplxEqual(v1, e3.Values(nil)) {
    95  			t.Errorf("eigenvector mismatch. Case %v", i)
    96  		}
    97  		if !cmplxEqual(v1, e4.Values(nil)) {
    98  			t.Errorf("eigenvector mismatch. Case %v", i)
    99  		}
   100  		var right2 CDense
   101  		e2.VectorsTo(&right2)
   102  		if !CEqual(&right, &right2) {
   103  			t.Errorf("right eigenvector mismatch. Case %v", i)
   104  		}
   105  		var left3 CDense
   106  		e3.LeftVectorsTo(&left3)
   107  		if !CEqual(&left, &left3) {
   108  			t.Errorf("left eigenvector mismatch. Case %v", i)
   109  		}
   110  
   111  		// TODO(btracey): Also add in a test for correctness when #308 is
   112  		// resolved and we have a CMat.Mul().
   113  	}
   114  }
   115  
   116  func cmplxEqual(v1, v2 []complex128) bool {
   117  	for i, v := range v1 {
   118  		if v != v2[i] {
   119  			return false
   120  		}
   121  	}
   122  	return true
   123  }
   124  
   125  func cmplxEqualTol(v1, v2 []complex128, tol float64) bool {
   126  	for i, v := range v1 {
   127  		if !cEqualWithinAbsOrRel(v, v2[i], tol, tol) {
   128  			return false
   129  		}
   130  	}
   131  	return true
   132  }
   133  
   134  func TestSymEigen(t *testing.T) {
   135  	t.Parallel()
   136  	// Hand coded tests with results from lapack.
   137  	for _, test := range []struct {
   138  		mat *SymDense
   139  
   140  		values  []float64
   141  		vectors *Dense
   142  	}{
   143  		{
   144  			mat:    NewSymDense(3, []float64{8, 2, 4, 2, 6, 10, 4, 10, 5}),
   145  			values: []float64{-4.707679201365891, 6.294580208480216, 17.413098992885672},
   146  			vectors: NewDense(3, 3, []float64{
   147  				-0.127343483135656, -0.902414161226903, -0.411621572466779,
   148  				-0.664177720955769, 0.385801900032553, -0.640331827193739,
   149  				0.736648893495999, 0.191847792659746, -0.648492738712395,
   150  			}),
   151  		},
   152  	} {
   153  		var es EigenSym
   154  		ok := es.Factorize(test.mat, true)
   155  		if !ok {
   156  			t.Errorf("bad factorization")
   157  		}
   158  		if !floats.EqualApprox(test.values, es.values, 1e-14) {
   159  			t.Errorf("Eigenvalue mismatch")
   160  		}
   161  		if !EqualApprox(test.vectors, es.vectors, 1e-14) {
   162  			t.Errorf("Eigenvector mismatch")
   163  		}
   164  
   165  		var es2 EigenSym
   166  		es2.Factorize(test.mat, false)
   167  		if !floats.EqualApprox(es2.values, es.values, 1e-14) {
   168  			t.Errorf("Eigenvalue mismatch when no vectors computed")
   169  		}
   170  	}
   171  
   172  	// Randomized tests
   173  	rnd := rand.New(rand.NewSource(1))
   174  	for _, n := range []int{3, 5, 10, 70} {
   175  		for cas := 0; cas < 10; cas++ {
   176  			a := make([]float64, n*n)
   177  			for i := range a {
   178  				a[i] = rnd.NormFloat64()
   179  			}
   180  			s := NewSymDense(n, a)
   181  			var es EigenSym
   182  			ok := es.Factorize(s, true)
   183  			if !ok {
   184  				t.Errorf("Bad test")
   185  			}
   186  
   187  			// Check that the eigenvectors are orthonormal.
   188  			if !isOrthonormal(es.vectors, 1e-8) {
   189  				t.Errorf("Eigenvectors not orthonormal")
   190  			}
   191  
   192  			// Check that the eigenvalues are actually eigenvalues.
   193  			for i := 0; i < n; i++ {
   194  				v := NewVecDense(n, Col(nil, i, es.vectors))
   195  				var m VecDense
   196  				m.MulVec(s, v)
   197  
   198  				var scal VecDense
   199  				scal.ScaleVec(es.values[i], v)
   200  
   201  				if !EqualApprox(&m, &scal, 1e-8) {
   202  					t.Errorf("Eigenvalue does not match")
   203  				}
   204  			}
   205  
   206  			// Check that the eigenvalues are in ascending order.
   207  			if !sort.Float64sAreSorted(es.values) {
   208  				t.Errorf("Eigenvalues not ascending")
   209  			}
   210  		}
   211  	}
   212  }