// Copyright ©2016 The Gonum Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package testlapack import ( "math" "golang.org/x/exp/rand" "gonum.org/v1/gonum/blas/blas64" ) // A123 is the non-symmetric singular matrix // [ 1 2 3 ] // A = [ 4 5 6 ] // [ 7 8 9 ] // It has three distinct real eigenvalues. type A123 struct{} func (A123) Matrix() blas64.General { return blas64.General{ Rows: 3, Cols: 3, Stride: 3, Data: []float64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, }, } } func (A123) Eigenvalues() []complex128 { return []complex128{16.116843969807043, -1.116843969807043, 0} } func (A123) LeftEV() blas64.General { return blas64.General{ Rows: 3, Cols: 3, Stride: 3, Data: []float64{ -0.464547273387671, -0.570795531228578, -0.677043789069485, -0.882905959653586, -0.239520420054206, 0.403865119545174, 0.408248290463862, -0.816496580927726, 0.408248290463863, }, } } func (A123) RightEV() blas64.General { return blas64.General{ Rows: 3, Cols: 3, Stride: 3, Data: []float64{ -0.231970687246286, -0.785830238742067, 0.408248290463864, -0.525322093301234, -0.086751339256628, -0.816496580927726, -0.818673499356181, 0.612327560228810, 0.408248290463863, }, } } // AntisymRandom is a anti-symmetric random matrix. All its eigenvalues are // imaginary with one zero if the order is odd. type AntisymRandom struct { mat blas64.General } func NewAntisymRandom(n int, rnd *rand.Rand) AntisymRandom { a := zeros(n, n, n) for i := 0; i < n; i++ { for j := i + 1; j < n; j++ { r := rnd.NormFloat64() a.Data[i*a.Stride+j] = r a.Data[j*a.Stride+i] = -r } } return AntisymRandom{a} } func (a AntisymRandom) Matrix() blas64.General { return cloneGeneral(a.mat) } func (AntisymRandom) Eigenvalues() []complex128 { return nil } // Circulant is a generally non-symmetric matrix given by // A[i,j] = 1 + (j-i+n)%n. // For example, for n=5, // [ 1 2 3 4 5 ] // [ 5 1 2 3 4 ] // A = [ 4 5 1 2 3 ] // [ 3 4 5 1 2 ] // [ 2 3 4 5 1 ] // It has real and complex eigenvalues, some possibly repeated. type Circulant int func (c Circulant) Matrix() blas64.General { n := int(c) a := zeros(n, n, n) for i := 0; i < n; i++ { for j := 0; j < n; j++ { a.Data[i*a.Stride+j] = float64(1 + (j-i+n)%n) } } return a } func (c Circulant) Eigenvalues() []complex128 { n := int(c) w := rootsOfUnity(n) ev := make([]complex128, n) for k := 0; k < n; k++ { ev[k] = complex(float64(n), 0) } for i := n - 1; i > 0; i-- { for k := 0; k < n; k++ { ev[k] = ev[k]*w[k] + complex(float64(i), 0) } } return ev } // Clement is a generally non-symmetric matrix given by // A[i,j] = i+1, if j == i+1, // = n-i, if j == i-1, // = 0, otherwise. // For example, for n=5, // [ . 1 . . . ] // [ 4 . 2 . . ] // A = [ . 3 . 3 . ] // [ . . 2 . 4 ] // [ . . . 1 . ] // It has n distinct real eigenvalues. type Clement int func (c Clement) Matrix() blas64.General { n := int(c) a := zeros(n, n, n) for i := 0; i < n; i++ { if i < n-1 { a.Data[i*a.Stride+i+1] = float64(i + 1) } if i > 0 { a.Data[i*a.Stride+i-1] = float64(n - i) } } return a } func (c Clement) Eigenvalues() []complex128 { n := int(c) ev := make([]complex128, n) for i := range ev { ev[i] = complex(float64(-n+2*i+1), 0) } return ev } // Creation is a singular non-symmetric matrix given by // A[i,j] = i, if j == i-1, // = 0, otherwise. // For example, for n=5, // [ . . . . . ] // [ 1 . . . . ] // A = [ . 2 . . . ] // [ . . 3 . . ] // [ . . . 4 . ] // Zero is its only eigenvalue. type Creation int func (c Creation) Matrix() blas64.General { n := int(c) a := zeros(n, n, n) for i := 1; i < n; i++ { a.Data[i*a.Stride+i-1] = float64(i) } return a } func (c Creation) Eigenvalues() []complex128 { return make([]complex128, int(c)) } // Diagonal is a diagonal matrix given by // A[i,j] = i+1, if i == j, // = 0, otherwise. // For example, for n=5, // [ 1 . . . . ] // [ . 2 . . . ] // A = [ . . 3 . . ] // [ . . . 4 . ] // [ . . . . 5 ] // It has n real eigenvalues {1,...,n}. type Diagonal int func (d Diagonal) Matrix() blas64.General { n := int(d) a := zeros(n, n, n) for i := 0; i < n; i++ { a.Data[i*a.Stride+i] = float64(i) } return a } func (d Diagonal) Eigenvalues() []complex128 { n := int(d) ev := make([]complex128, n) for i := range ev { ev[i] = complex(float64(i), 0) } return ev } // Downshift is a non-singular upper Hessenberg matrix given by // A[i,j] = 1, if (i-j+n)%n == 1, // = 0, otherwise. // For example, for n=5, // [ . . . . 1 ] // [ 1 . . . . ] // A = [ . 1 . . . ] // [ . . 1 . . ] // [ . . . 1 . ] // Its eigenvalues are the complex roots of unity. type Downshift int func (d Downshift) Matrix() blas64.General { n := int(d) a := zeros(n, n, n) a.Data[n-1] = 1 for i := 1; i < n; i++ { a.Data[i*a.Stride+i-1] = 1 } return a } func (d Downshift) Eigenvalues() []complex128 { return rootsOfUnity(int(d)) } // Fibonacci is an upper Hessenberg matrix with 3 distinct real eigenvalues. For // example, for n=5, // [ . 1 . . . ] // [ 1 1 . . . ] // A = [ . 1 1 . . ] // [ . . 1 1 . ] // [ . . . 1 1 ] type Fibonacci int func (f Fibonacci) Matrix() blas64.General { n := int(f) a := zeros(n, n, n) if n > 1 { a.Data[1] = 1 } for i := 1; i < n; i++ { a.Data[i*a.Stride+i-1] = 1 a.Data[i*a.Stride+i] = 1 } return a } func (f Fibonacci) Eigenvalues() []complex128 { n := int(f) ev := make([]complex128, n) if n == 0 || n == 1 { return ev } phi := 0.5 * (1 + math.Sqrt(5)) ev[0] = complex(phi, 0) for i := 1; i < n-1; i++ { ev[i] = 1 + 0i } ev[n-1] = complex(1-phi, 0) return ev } // Gear is a singular non-symmetric matrix with real eigenvalues. For example, // for n=5, // [ . 1 . . 1 ] // [ 1 . 1 . . ] // A = [ . 1 . 1 . ] // [ . . 1 . 1 ] // [-1 . . 1 . ] type Gear int func (g Gear) Matrix() blas64.General { n := int(g) a := zeros(n, n, n) if n == 1 { return a } for i := 0; i < n-1; i++ { a.Data[i*a.Stride+i+1] = 1 } for i := 1; i < n; i++ { a.Data[i*a.Stride+i-1] = 1 } a.Data[n-1] = 1 a.Data[(n-1)*a.Stride] = -1 return a } func (g Gear) Eigenvalues() []complex128 { n := int(g) ev := make([]complex128, n) if n == 0 || n == 1 { return ev } if n == 2 { ev[0] = complex(0, 1) ev[1] = complex(0, -1) return ev } w := 0 ev[w] = math.Pi / 2 w++ phi := (n - 1) / 2 for p := 1; p <= phi; p++ { ev[w] = complex(float64(2*p)*math.Pi/float64(n), 0) w++ } phi = n / 2 for p := 1; p <= phi; p++ { ev[w] = complex(float64(2*p-1)*math.Pi/float64(n), 0) w++ } for i, v := range ev { ev[i] = complex(2*math.Cos(real(v)), 0) } return ev } // Grcar is an upper Hessenberg matrix given by // A[i,j] = -1 if i == j+1, // = 1 if i <= j and j <= i+k, // = 0 otherwise. // For example, for n=5 and k=2, // [ 1 1 1 . . ] // [ -1 1 1 1 . ] // A = [ . -1 1 1 1 ] // [ . . -1 1 1 ] // [ . . . -1 1 ] // The matrix has sensitive eigenvalues but they are not given explicitly. type Grcar struct { N int K int } func (g Grcar) Matrix() blas64.General { n := g.N a := zeros(n, n, n) for k := 0; k <= g.K; k++ { for i := 0; i < n-k; i++ { a.Data[i*a.Stride+i+k] = 1 } } for i := 1; i < n; i++ { a.Data[i*a.Stride+i-1] = -1 } return a } func (Grcar) Eigenvalues() []complex128 { return nil } // Hanowa is a non-symmetric non-singular matrix of even order given by // A[i,j] = alpha if i == j, // = -i-1 if i < n/2 and j == i + n/2, // = i+1-n/2 if i >= n/2 and j == i - n/2, // = 0 otherwise. // The matrix has complex eigenvalues. type Hanowa struct { N int // Order of the matrix, must be even. Alpha float64 } func (h Hanowa) Matrix() blas64.General { if h.N&0x1 != 0 { panic("lapack: matrix order must be even") } n := h.N a := zeros(n, n, n) for i := 0; i < n; i++ { a.Data[i*a.Stride+i] = h.Alpha } for i := 0; i < n/2; i++ { a.Data[i*a.Stride+i+n/2] = float64(-i - 1) } for i := n / 2; i < n; i++ { a.Data[i*a.Stride+i-n/2] = float64(i + 1 - n/2) } return a } func (h Hanowa) Eigenvalues() []complex128 { if h.N&0x1 != 0 { panic("lapack: matrix order must be even") } n := int(h.N) ev := make([]complex128, n) for i := 0; i < n/2; i++ { ev[2*i] = complex(h.Alpha, float64(-i-1)) ev[2*i+1] = complex(h.Alpha, float64(i+1)) } return ev } // Lesp is a tridiagonal, generally non-symmetric matrix given by // A[i,j] = -2*i-5 if i == j, // = 1/(i+1) if i == j-1, // = j+1 if i == j+1. // For example, for n=5, // [ -5 2 . . . ] // [ 1/2 -7 3 . . ] // A = [ . 1/3 -9 4 . ] // [ . . 1/4 -11 5 ] // [ . . . 1/5 -13 ]. // The matrix has sensitive eigenvalues but they are not given explicitly. type Lesp int func (l Lesp) Matrix() blas64.General { n := int(l) a := zeros(n, n, n) for i := 0; i < n; i++ { a.Data[i*a.Stride+i] = float64(-2*i - 5) } for i := 0; i < n-1; i++ { a.Data[i*a.Stride+i+1] = float64(i + 2) } for i := 1; i < n; i++ { a.Data[i*a.Stride+i-1] = 1 / float64(i+1) } return a } func (Lesp) Eigenvalues() []complex128 { return nil } // Rutis is the 4×4 non-symmetric matrix // [ 4 -5 0 3 ] // A = [ 0 4 -3 -5 ] // [ 5 -3 4 0 ] // [ 3 0 5 4 ] // It has two distinct real eigenvalues and a pair of complex eigenvalues. type Rutis struct{} func (Rutis) Matrix() blas64.General { return blas64.General{ Rows: 4, Cols: 4, Stride: 4, Data: []float64{ 4, -5, 0, 3, 0, 4, -3, -5, 5, -3, 4, 0, 3, 0, 5, 4, }, } } func (Rutis) Eigenvalues() []complex128 { return []complex128{12, 1 + 5i, 1 - 5i, 2} } // Tris is a tridiagonal matrix given by // A[i,j] = x if i == j-1, // = y if i == j, // = z if i == j+1. // If x*z is negative, the matrix has complex eigenvalues. type Tris struct { N int X, Y, Z float64 } func (t Tris) Matrix() blas64.General { n := t.N a := zeros(n, n, n) for i := 1; i < n; i++ { a.Data[i*a.Stride+i-1] = t.X } for i := 0; i < n; i++ { a.Data[i*a.Stride+i] = t.Y } for i := 0; i < n-1; i++ { a.Data[i*a.Stride+i+1] = t.Z } return a } func (t Tris) Eigenvalues() []complex128 { n := int(t.N) ev := make([]complex128, n) for i := range ev { angle := float64(i+1) * math.Pi / float64(n+1) arg := t.X * t.Z if arg >= 0 { ev[i] = complex(t.Y+2*math.Sqrt(arg)*math.Cos(angle), 0) } else { ev[i] = complex(t.Y, 2*math.Sqrt(-arg)*math.Cos(angle)) } } return ev } // Wilk4 is a 4×4 lower triangular matrix with 4 distinct real eigenvalues. type Wilk4 struct{} func (Wilk4) Matrix() blas64.General { return blas64.General{ Rows: 4, Cols: 4, Stride: 4, Data: []float64{ 0.9143e-4, 0.0, 0.0, 0.0, 0.8762, 0.7156e-4, 0.0, 0.0, 0.7943, 0.8143, 0.9504e-4, 0.0, 0.8017, 0.6123, 0.7165, 0.7123e-4, }, } } func (Wilk4) Eigenvalues() []complex128 { return []complex128{ 0.9504e-4, 0.9143e-4, 0.7156e-4, 0.7123e-4, } } // Wilk12 is a 12×12 lower Hessenberg matrix with 12 distinct real eigenvalues. type Wilk12 struct{} func (Wilk12) Matrix() blas64.General { return blas64.General{ Rows: 12, Cols: 12, Stride: 12, Data: []float64{ 12, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 11, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 10, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 8, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 7, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 7, 7, 6, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 5, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 4, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }, } } func (Wilk12) Eigenvalues() []complex128 { return []complex128{ 32.2288915015722210, 20.1989886458770691, 12.3110774008685340, 6.9615330855671154, 3.5118559485807528, 1.5539887091319704, 0.6435053190136506, 0.2847497205488856, 0.1436465181918488, 0.0812276683076552, 0.0495074140194613, 0.0310280683208907, } } // Wilk20 is a 20×20 lower Hessenberg matrix. If the parameter is 0, the matrix // has 20 distinct real eigenvalues. If the parameter is 1e-10, the matrix has 6 // real eigenvalues and 7 pairs of complex eigenvalues. type Wilk20 float64 func (w Wilk20) Matrix() blas64.General { a := zeros(20, 20, 20) for i := 0; i < 20; i++ { a.Data[i*a.Stride+i] = float64(i + 1) } for i := 0; i < 19; i++ { a.Data[i*a.Stride+i+1] = 20 } a.Data[19*a.Stride] = float64(w) return a } func (w Wilk20) Eigenvalues() []complex128 { if float64(w) == 0 { ev := make([]complex128, 20) for i := range ev { ev[i] = complex(float64(i+1), 0) } return ev } return nil } // Zero is a matrix with all elements equal to zero. type Zero int func (z Zero) Matrix() blas64.General { n := int(z) return zeros(n, n, n) } func (z Zero) Eigenvalues() []complex128 { n := int(z) return make([]complex128, n) }