mirror of
https://github.com/gonum/gonum.git
synced 2025-10-05 15:16:59 +08:00
168 lines
5.3 KiB
Go
168 lines
5.3 KiB
Go
// Copyright ©2021 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 (
|
|
"fmt"
|
|
"math"
|
|
"testing"
|
|
|
|
"golang.org/x/exp/rand"
|
|
"gonum.org/v1/gonum/blas/blas64"
|
|
)
|
|
|
|
type Dlag2er interface {
|
|
Dlag2(a []float64, lda int, b []float64, ldb int) (scale1, scale2, wr1, wr2, wi float64)
|
|
}
|
|
|
|
func Dlag2Test(t *testing.T, impl Dlag2er) {
|
|
rnd := rand.New(rand.NewSource(1))
|
|
for _, lda := range []int{2, 5} {
|
|
for _, ldb := range []int{2, 5} {
|
|
for i := 0; i <= 80; i++ { // 80 iterations for variability.
|
|
dlag2Test(t, impl, rnd, lda, ldb)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func dlag2Test(t *testing.T, impl Dlag2er, rnd *rand.Rand, lda, ldb int) {
|
|
const tol = 1e-14
|
|
a := randomGeneral(2, 2, lda, rnd)
|
|
b := randomGeneral(2, 2, ldb, rnd)
|
|
a11 := a.Data[0]
|
|
a12 := a.Data[1]
|
|
a21 := a.Data[lda]
|
|
a22 := a.Data[lda+1]
|
|
b11 := b.Data[0]
|
|
b12 := b.Data[1]
|
|
b22 := b.Data[ldb+1]
|
|
name := fmt.Sprintf("lda=%d, ldb=%d", lda, ldb)
|
|
scale1, scale2, wr1, wr2, wi := impl.Dlag2(a.Data, a.Stride, b.Data, b.Stride)
|
|
if wi < 0 {
|
|
t.Errorf("%v: wi can not be negative wi=%v", name, wi)
|
|
}
|
|
|
|
dget53Test(t, a, b, scale1, wr1, wi)
|
|
dget53Test(t, a, b, scale2, wr2, wi)
|
|
|
|
// Complex eigenvalue solution.
|
|
if wi > 0 {
|
|
if wr1 != wr2 {
|
|
t.Errorf("%v: wr1 should equal wr2 for complex eigenvalues. got %v!=%v", name, wr1, wr2)
|
|
}
|
|
idet1 := cmplxdet2x2(complex(scale1*a11-wr1*b11, -wi*b11), complex(scale1*a12-wr1*b12, -wi*b12),
|
|
complex(scale1*a21, 0), complex(scale1*a22-wr1*b22, -wi*b22))
|
|
idet2 := cmplxdet2x2(complex(scale1*a11-wr1*b11, wi*b11), complex(scale1*a12-wr1*b12, wi*b12),
|
|
complex(scale1*a21, 0), complex(scale1*a22-wr1*b22, wi*b22))
|
|
|
|
if math.Abs(real(idet1))+math.Abs(imag(idet1)) > tol || math.Abs(real(idet2))+math.Abs(imag(idet2)) > tol {
|
|
t.Errorf("%v: (%.4v±%.4vi)/%.4v did not solve eigenvalue problem", name, wr1, wi, scale1)
|
|
}
|
|
return // Complex solution verified
|
|
}
|
|
|
|
// Real eigenvalue solution.
|
|
// |s*A - w*B| = 0 is solution for eigenvalue problem. Calculate distance from solution.
|
|
res1 := det2x2(scale1*a11-wr1*b11, scale1*a12-wr1*b12,
|
|
scale1*a21, scale1*a22-wr1*b22)
|
|
if math.Abs(res1) > tol {
|
|
t.Errorf("%v: got a residual from |%0.4v*A - w1*B| > tol: %v", name, scale1, res1)
|
|
}
|
|
res2 := det2x2(scale2*a11-wr2*b11, scale2*a12-wr2*b12,
|
|
scale2*a21, scale2*a22-wr2*b22)
|
|
if math.Abs(res2) > tol {
|
|
t.Errorf("%v: got a residual from |s*A - w2*B| > tol: %v", name, res1)
|
|
}
|
|
}
|
|
|
|
// dget53Test checks the generalized eigenvalues computed by dlag2.
|
|
// The basic test for an eigenvalue is:
|
|
// | det( s*A - w*B ) |
|
|
// result = ---------------------------------------------------
|
|
// ulp max( s*norm(A), |w|*norm(B) )*norm( s*A - w*B )
|
|
//
|
|
// If s and w cant be scaled result will be 1/ulp.
|
|
func dget53Test(t *testing.T, a, b blas64.General, scale, wr, wi float64) (result float64) {
|
|
lda := a.Stride
|
|
ldb := b.Stride
|
|
// Machine constants and norms.
|
|
safmin := dlamchS
|
|
ulp := dlamchE * dlamchB
|
|
absw := math.Abs(wr) + math.Abs(wi)
|
|
anorm := math.Max(math.Abs(a.Data[0])+math.Abs(a.Data[1]), math.Max(math.Abs(a.Data[lda])+math.Abs(a.Data[lda+1]), safmin))
|
|
bnorm := math.Max(math.Abs(b.Data[0]), math.Max(math.Abs(b.Data[ldb])+math.Abs(b.Data[ldb+1]), safmin))
|
|
|
|
// Check for possible overflow.
|
|
temp := (safmin*bnorm)*absw + (safmin*anorm)*scale
|
|
|
|
scales := scale
|
|
wrs := wr
|
|
wis := wi
|
|
if temp >= 1 {
|
|
t.Error("dget53: s*norm(A) + |w|*norm(B) > 1/safe_minimum")
|
|
temp = 1 / temp
|
|
scales *= temp
|
|
wrs *= temp
|
|
wis *= temp
|
|
absw = math.Abs(wrs) + math.Abs(wis)
|
|
}
|
|
s1 := math.Max(ulp*math.Max(scales*anorm, absw*bnorm), safmin*math.Max(scales, absw))
|
|
|
|
// Check for W and SCALE essentially zero.
|
|
if s1 < safmin {
|
|
t.Error("dget53: ulp*max( s*norm(A), |w|*norm(B) ) < safe_minimum")
|
|
if scales < safmin && absw < safmin {
|
|
t.Error("dget53: s and w could not be scaled so as to compute test")
|
|
return 1 / ulp
|
|
}
|
|
// Scale up to avoid underflow.
|
|
temp = 1 / math.Max(scales*anorm+absw*bnorm, safmin)
|
|
scales *= temp
|
|
wrs *= temp
|
|
wis *= temp
|
|
absw = math.Abs(wrs) + math.Abs(wis)
|
|
s1 = math.Max(ulp*math.Max(scales*anorm, absw*bnorm),
|
|
safmin*math.Max(scales, absw))
|
|
if s1 < safmin {
|
|
t.Error("dget53: s and w could not be scaled so as to compute test")
|
|
return 1 / ulp
|
|
}
|
|
}
|
|
|
|
// Compute C = s*A - w*B.
|
|
cr11 := scales*a.Data[0] - wrs*b.Data[0]
|
|
ci11 := -wis * b.Data[0]
|
|
cr21 := scales * a.Data[1]
|
|
cr12 := scales*a.Data[lda] - wrs*b.Data[ldb]
|
|
ci12 := -wis * b.Data[ldb]
|
|
cr22 := scales*a.Data[lda+1] - wrs*b.Data[ldb+1]
|
|
ci22 := -wis * b.Data[ldb+1]
|
|
|
|
// Compute the smallest singular value of s*A - w*B:
|
|
// |det( s*A - w*B )|
|
|
// sigma_min = ------------------
|
|
// norm( s*A - w*B )
|
|
cnorm := math.Max(math.Abs(cr11)+math.Abs(ci11)+math.Abs(cr21),
|
|
math.Max(math.Abs(cr12)+math.Abs(ci12)+math.Abs(cr22)+math.Abs(ci22), safmin))
|
|
cscale := 1 / math.Sqrt(cnorm)
|
|
detr := (cscale*cr11)*(cscale*cr22) -
|
|
(cscale*ci11)*(cscale*ci22) -
|
|
(cscale*cr12)*(cscale*cr21)
|
|
deti := (cscale*cr11)*(cscale*ci22) +
|
|
(cscale*ci11)*(cscale*cr22) -
|
|
(cscale*ci12)*(cscale*cr21)
|
|
sigmin := math.Abs(detr) + math.Abs(deti)
|
|
result = sigmin / s1
|
|
return result
|
|
}
|
|
|
|
// cmplxdet2x2 returns the determinant of
|
|
// |a11 a12|
|
|
// |a21 a22|
|
|
func cmplxdet2x2(a11, a12, a21, a22 complex128) complex128 {
|
|
return a11*a22 - a12*a21
|
|
}
|