Files
gonum/lapack/testlapack/test_matrices.go
2025-02-01 22:18:04 +10:30

654 lines
13 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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"
"math/rand/v2"
"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 := 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 := 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)
}