Files
gonum/testlapack/dlaqr2.go
Vladimir Chalupecky b125291e21 native: check input slices to Dlaqr2, Dgehrd and Dormhr only after workspace query
so that it is possible to pass all slices as nil to those functions. A workspace
query should depend only on the input sizes, the matrices and vectors must
not be accessed anyway or may not be available at the call site. Moreover, often
the reference LAPACK uses arbitrary placeholders for arrays and relies on the
fact that no checking is done.
2016-08-20 11:05:19 +09:00

326 lines
7.8 KiB
Go

// 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 (
"fmt"
"math/rand"
"testing"
"github.com/gonum/blas"
"github.com/gonum/blas/blas64"
)
type Dlaqr2er interface {
Dlaqr2(wantt, wantz bool, n, ktop, kbot, nw int, h []float64, ldh int, iloz, ihiz int, z []float64, ldz int, sr, si []float64, v []float64, ldv int, nh int, t []float64, ldt int, nv int, wv []float64, ldwv int, work []float64, lwork int) (ns, nd int)
}
type dlaqr2Test struct {
wantt, wantz bool
ktop, kbot int
nw int
h blas64.General
iloz, ihiz int
evWant []complex128 // Optional slice with known eigenvalues.
}
func Dlaqr2Test(t *testing.T, impl Dlaqr2er) {
rnd := rand.New(rand.NewSource(1))
for _, wantt := range []bool{true, false} {
for _, wantz := range []bool{true, false} {
for _, n := range []int{1, 2, 3, 4, 5, 6, 10, 18, 31, 53} {
for _, extra := range []int{0, 1, 11} {
for cas := 0; cas < 100; cas++ {
ktop := rnd.Intn(n)
kbot := rnd.Intn(n)
if ktop > kbot {
ktop, kbot = kbot, ktop
}
nw := rnd.Intn(kbot-ktop+1) + 1
h := randomHessenberg(n, n+extra, rnd)
if ktop-1 >= 0 {
h.Data[ktop*h.Stride+ktop-1] = 0
}
if kbot+1 < n {
h.Data[(kbot+1)*h.Stride+kbot] = 0
}
iloz := rnd.Intn(ktop + 1)
ihiz := kbot + rnd.Intn(n-kbot)
test := dlaqr2Test{
wantt: wantt,
wantz: wantz,
ktop: ktop,
kbot: kbot,
nw: nw,
h: h,
iloz: iloz,
ihiz: ihiz,
}
testDlaqr2(t, impl, test, false, rnd)
testDlaqr2(t, impl, test, true, rnd)
}
}
}
}
}
// Tests with n=0.
for _, wantt := range []bool{true, false} {
for _, wantz := range []bool{true, false} {
for _, extra := range []int{0, 1, 11} {
test := dlaqr2Test{
wantt: wantt,
wantz: wantz,
h: randomHessenberg(0, extra, rnd),
ktop: 0,
kbot: -1,
iloz: 0,
ihiz: -1,
nw: 0,
}
testDlaqr2(t, impl, test, true, rnd)
testDlaqr2(t, impl, test, false, rnd)
}
}
}
// Tests with explicit eigenvalues computed by Octave.
for _, test := range []dlaqr2Test{
{
h: blas64.General{
Rows: 1,
Cols: 1,
Stride: 1,
Data: []float64{7.09965484086874e-1},
},
ktop: 0,
kbot: 0,
iloz: 0,
ihiz: 0,
evWant: []complex128{7.09965484086874e-1},
},
{
h: blas64.General{
Rows: 2,
Cols: 2,
Stride: 2,
Data: []float64{
0, -1,
1, 0,
},
},
ktop: 0,
kbot: 1,
iloz: 0,
ihiz: 1,
evWant: []complex128{1i, -1i},
},
{
h: blas64.General{
Rows: 2,
Cols: 2,
Stride: 2,
Data: []float64{
6.25219991450918e-1, 8.17510791994361e-1,
3.31218891622294e-1, 1.24103744878131e-1,
},
},
ktop: 0,
kbot: 1,
iloz: 0,
ihiz: 1,
evWant: []complex128{9.52203547663447e-1, -2.02879811334398e-1},
},
{
h: blas64.General{
Rows: 4,
Cols: 4,
Stride: 4,
Data: []float64{
1, 0, 0, 0,
0, 6.25219991450918e-1, 8.17510791994361e-1, 0,
0, 3.31218891622294e-1, 1.24103744878131e-1, 0,
0, 0, 0, 1,
},
},
ktop: 1,
kbot: 2,
iloz: 0,
ihiz: 3,
evWant: []complex128{9.52203547663447e-1, -2.02879811334398e-1},
},
{
h: blas64.General{
Rows: 2,
Cols: 2,
Stride: 2,
Data: []float64{
-1.1219562276608, 6.85473513349362e-1,
-8.19951061145131e-1, 1.93728523178888e-1,
},
},
ktop: 0,
kbot: 1,
iloz: 0,
ihiz: 1,
evWant: []complex128{
-4.64113852240958e-1 + 3.59580510817350e-1i,
-4.64113852240958e-1 - 3.59580510817350e-1i,
},
},
{
h: blas64.General{
Rows: 5,
Cols: 5,
Stride: 5,
Data: []float64{
9.57590178533658e-1, -5.10651295522708e-1, 9.24974510015869e-1, -1.30016306879522e-1, 2.92601986926954e-2,
-1.08084756637964, 1.77529701001213, -1.36480197632509, 2.23196371219601e-1, 1.12912853063308e-1,
0, -8.44075612174676e-1, 1.067867614486, -2.55782915176399e-1, -2.00598563137468e-1,
0, 0, -5.67097237165410e-1, 2.07205057427341e-1, 6.54998340743380e-1,
0, 0, 0, -1.89441413886041e-1, -4.18125416021786e-1,
},
},
ktop: 0,
kbot: 4,
iloz: 0,
ihiz: 4,
evWant: []complex128{
2.94393309555622,
4.97029793606701e-1 + 3.63041654992384e-1i,
4.97029793606701e-1 - 3.63041654992384e-1i,
-1.74079119166145e-1 + 2.01570009462092e-1i,
-1.74079119166145e-1 - 2.01570009462092e-1i,
},
},
} {
test.wantt = true
test.wantz = true
test.nw = test.kbot - test.ktop + 1
testDlaqr2(t, impl, test, true, rnd)
testDlaqr2(t, impl, test, false, rnd)
}
}
func testDlaqr2(t *testing.T, impl Dlaqr2er, test dlaqr2Test, opt bool, rnd *rand.Rand) {
const tol = 1e-14
h := cloneGeneral(test.h)
n := h.Cols
extra := h.Stride - h.Cols
wantt := test.wantt
wantz := test.wantz
ktop := test.ktop
kbot := test.kbot
nw := test.nw
iloz := test.iloz
ihiz := test.ihiz
var z, zCopy blas64.General
if wantz {
z = eye(n, n+extra)
zCopy = cloneGeneral(z)
}
sr := nanSlice(kbot + 1)
si := nanSlice(kbot + 1)
v := randomGeneral(nw, nw, nw+extra, rnd)
var nh int
if nw > 0 {
nh = nw + rnd.Intn(nw) // nh must be at least nw.
}
tmat := randomGeneral(nw, nh, nh+extra, rnd)
var nv int
if nw > 0 {
nv = rnd.Intn(nw) + 1
}
wv := randomGeneral(nv, nw, nw+extra, rnd)
var work []float64
if opt {
work = nanSlice(1)
impl.Dlaqr2(wantt, wantz, n, ktop, kbot, nw, nil, h.Stride, iloz, ihiz, nil, z.Stride,
nil, nil, nil, v.Stride, tmat.Cols, nil, tmat.Stride, wv.Rows, nil, wv.Stride, work, -1)
work = nanSlice(int(work[0]))
} else {
work = nanSlice(max(1, 2*nw))
}
ns, nd := impl.Dlaqr2(wantt, wantz, n, ktop, kbot, nw, h.Data, h.Stride, iloz, ihiz, z.Data, z.Stride,
sr, si, v.Data, v.Stride, tmat.Cols, tmat.Data, tmat.Stride, wv.Rows, wv.Data, wv.Stride, work, len(work))
prefix := fmt.Sprintf("Case wantt=%v, wantz=%v, n=%v, ktop=%v, kbot=%v, nw=%v, iloz=%v, ihiz=%v, extra=%v",
wantt, wantz, n, ktop, kbot, nw, iloz, ihiz, extra)
if !generalOutsideAllNaN(h) {
t.Errorf("%v: out-of-range write to H\n%v", prefix, h.Data)
}
if !generalOutsideAllNaN(z) {
t.Errorf("%v: out-of-range write to Z\n%v", prefix, z.Data)
}
if !generalOutsideAllNaN(v) {
t.Errorf("%v: out-of-range write to V\n%v", prefix, v.Data)
}
if !generalOutsideAllNaN(tmat) {
t.Errorf("%v: out-of-range write to T\n%v", prefix, tmat.Data)
}
if !generalOutsideAllNaN(wv) {
t.Errorf("%v: out-of-range write to WV\n%v", prefix, wv.Data)
}
if !isAllNaN(sr[:kbot-nd-ns+1]) || !isAllNaN(sr[kbot+1:]) {
t.Errorf("%v: out-of-range write to sr")
}
if !isAllNaN(si[:kbot-nd-ns+1]) || !isAllNaN(si[kbot+1:]) {
t.Errorf("%v: out-of-range write to si")
}
if !isUpperHessenberg(h) {
t.Errorf("%v: H is not upper Hessenberg", prefix)
}
if test.evWant != nil {
for i := kbot - nd + 1; i <= kbot; i++ {
ev := complex(sr[i], si[i])
if !containsComplex(test.evWant, ev, tol) {
t.Errorf("%v: unexpected eigenvalue %v", prefix, ev)
}
}
}
if !wantz {
return
}
var zmod bool
for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
if z.Data[i*z.Stride+j] == zCopy.Data[i*zCopy.Stride+j] {
continue
}
if i < iloz || i > ihiz || j < kbot-nw+1 || j > kbot {
zmod = true
}
}
}
if zmod {
t.Errorf("%v: unexpected modification of Z", prefix)
}
if !isOrthonormal(z) {
t.Errorf("%v: Z is not orthogonal", prefix)
}
if wantt {
hu := eye(n, n)
blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, test.h, z, 0, hu)
uhu := eye(n, n)
blas64.Gemm(blas.Trans, blas.NoTrans, 1, z, hu, 0, uhu)
if !equalApproxGeneral(uhu, h, 10*tol) {
t.Errorf("%v: Z^T*(initial H)*Z and (final H) are not equal", prefix)
}
}
}