github.com/gonum/lapack@v0.0.0-20181123203213-e4cdc5a0bff9/testlapack/dlaexc.go (about) 1 // Copyright ©2016 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 testlapack 6 7 import ( 8 "fmt" 9 "math" 10 "math/cmplx" 11 "math/rand" 12 "testing" 13 14 "github.com/gonum/blas" 15 "github.com/gonum/blas/blas64" 16 ) 17 18 type Dlaexcer interface { 19 Dlaexc(wantq bool, n int, t []float64, ldt int, q []float64, ldq int, j1, n1, n2 int, work []float64) bool 20 } 21 22 func DlaexcTest(t *testing.T, impl Dlaexcer) { 23 rnd := rand.New(rand.NewSource(1)) 24 25 for _, wantq := range []bool{true, false} { 26 for _, n := range []int{1, 2, 3, 4, 5, 6, 10, 18, 31, 53} { 27 for _, extra := range []int{0, 1, 11} { 28 for cas := 0; cas < 100; cas++ { 29 j1 := rnd.Intn(n) 30 n1 := min(rnd.Intn(3), n-j1) 31 n2 := min(rnd.Intn(3), n-j1-n1) 32 testDlaexc(t, impl, wantq, n, j1, n1, n2, extra, rnd) 33 } 34 } 35 } 36 } 37 } 38 39 func testDlaexc(t *testing.T, impl Dlaexcer, wantq bool, n, j1, n1, n2, extra int, rnd *rand.Rand) { 40 const tol = 1e-14 41 42 tmat := randomGeneral(n, n, n+extra, rnd) 43 // Zero out the lower triangle. 44 for i := 1; i < n; i++ { 45 for j := 0; j < i; j++ { 46 tmat.Data[i*tmat.Stride+j] = 0 47 } 48 } 49 // Make any 2x2 diagonal block to be in Schur canonical form. 50 if n1 == 2 { 51 // Diagonal elements equal. 52 tmat.Data[(j1+1)*tmat.Stride+j1+1] = tmat.Data[j1*tmat.Stride+j1] 53 // Off-diagonal elements of opposite sign. 54 c := rnd.NormFloat64() 55 if math.Signbit(c) == math.Signbit(tmat.Data[j1*tmat.Stride+j1+1]) { 56 c *= -1 57 } 58 tmat.Data[(j1+1)*tmat.Stride+j1] = c 59 } 60 if n2 == 2 { 61 // Diagonal elements equal. 62 tmat.Data[(j1+n1+1)*tmat.Stride+j1+n1+1] = tmat.Data[(j1+n1)*tmat.Stride+j1+n1] 63 // Off-diagonal elements of opposite sign. 64 c := rnd.NormFloat64() 65 if math.Signbit(c) == math.Signbit(tmat.Data[(j1+n1)*tmat.Stride+j1+n1+1]) { 66 c *= -1 67 } 68 tmat.Data[(j1+n1+1)*tmat.Stride+j1+n1] = c 69 } 70 tmatCopy := cloneGeneral(tmat) 71 var q, qCopy blas64.General 72 if wantq { 73 q = eye(n, n+extra) 74 qCopy = cloneGeneral(q) 75 } 76 work := nanSlice(n) 77 78 ok := impl.Dlaexc(wantq, n, tmat.Data, tmat.Stride, q.Data, q.Stride, j1, n1, n2, work) 79 80 prefix := fmt.Sprintf("Case n=%v, j1=%v, n1=%v, n2=%v, wantq=%v, extra=%v", n, j1, n1, n2, wantq, extra) 81 82 if !generalOutsideAllNaN(tmat) { 83 t.Errorf("%v: out-of-range write to T", prefix) 84 } 85 if wantq && !generalOutsideAllNaN(q) { 86 t.Errorf("%v: out-of-range write to Q", prefix) 87 } 88 89 if !ok { 90 if n1 == 1 && n2 == 1 { 91 t.Errorf("%v: unexpected failure", prefix) 92 } else { 93 t.Logf("%v: Dlaexc returned false") 94 } 95 } 96 97 if !ok || n1 == 0 || n2 == 0 || j1+n1 >= n { 98 // Check that T is not modified. 99 for i := 0; i < n; i++ { 100 for j := 0; j < n; j++ { 101 if tmat.Data[i*tmat.Stride+j] != tmatCopy.Data[i*tmatCopy.Stride+j] { 102 t.Errorf("%v: ok == false but T[%v,%v] modified", prefix, i, j) 103 } 104 } 105 } 106 if !wantq { 107 return 108 } 109 // Check that Q is not modified. 110 for i := 0; i < n; i++ { 111 for j := 0; j < n; j++ { 112 if q.Data[i*q.Stride+j] != qCopy.Data[i*qCopy.Stride+j] { 113 t.Errorf("%v: ok == false but Q[%v,%v] modified", prefix, i, j) 114 } 115 } 116 } 117 return 118 } 119 120 // Check that T is not modified outside of rows and columns [j1:j1+n1+n2]. 121 for i := 0; i < n; i++ { 122 if j1 <= i && i < j1+n1+n2 { 123 continue 124 } 125 for j := 0; j < n; j++ { 126 if j1 <= j && j < j1+n1+n2 { 127 continue 128 } 129 diff := tmat.Data[i*tmat.Stride+j] - tmatCopy.Data[i*tmatCopy.Stride+j] 130 if diff != 0 { 131 t.Errorf("%v: unexpected modification of T[%v,%v]", prefix, i, j) 132 } 133 } 134 } 135 136 if n1 == 1 { 137 // 1×1 blocks are swapped exactly. 138 got := tmat.Data[(j1+n2)*tmat.Stride+j1+n2] 139 want := tmatCopy.Data[j1*tmatCopy.Stride+j1] 140 if want != got { 141 t.Errorf("%v: unexpected value of T[%v,%v]. Want %v, got %v", prefix, j1+n2, j1+n2, want, got) 142 } 143 } else { 144 // Check that the swapped 2×2 block is in Schur canonical form. 145 // The n1×n1 block is now located at T[j1+n2,j1+n2]. 146 a, b, c, d := extract2x2Block(tmat.Data[(j1+n2)*tmat.Stride+j1+n2:], tmat.Stride) 147 if !isSchurCanonical(a, b, c, d) { 148 t.Errorf("%v: 2×2 block at T[%v,%v] not in Schur canonical form", prefix, j1+n2, j1+n2) 149 } 150 ev1Got, ev2Got := schurBlockEigenvalues(a, b, c, d) 151 152 // Check that the swapped 2×2 block has the same eigenvalues. 153 // The n1×n1 block was originally located at T[j1,j1]. 154 a, b, c, d = extract2x2Block(tmatCopy.Data[j1*tmatCopy.Stride+j1:], tmatCopy.Stride) 155 ev1Want, ev2Want := schurBlockEigenvalues(a, b, c, d) 156 if cmplx.Abs(ev1Got-ev1Want) > tol { 157 t.Errorf("%v: unexpected first eigenvalue of 2×2 block at T[%v,%v]. Want %v, got %v", 158 prefix, j1+n2, j1+n2, ev1Want, ev1Got) 159 } 160 if cmplx.Abs(ev2Got-ev2Want) > tol { 161 t.Errorf("%v: unexpected second eigenvalue of 2×2 block at T[%v,%v]. Want %v, got %v", 162 prefix, j1+n2, j1+n2, ev2Want, ev2Got) 163 } 164 } 165 if n2 == 1 { 166 // 1×1 blocks are swapped exactly. 167 got := tmat.Data[j1*tmat.Stride+j1] 168 want := tmatCopy.Data[(j1+n1)*tmatCopy.Stride+j1+n1] 169 if want != got { 170 t.Errorf("%v: unexpected value of T[%v,%v]. Want %v, got %v", prefix, j1, j1, want, got) 171 } 172 } else { 173 // Check that the swapped 2×2 block is in Schur canonical form. 174 // The n2×n2 block is now located at T[j1,j1]. 175 a, b, c, d := extract2x2Block(tmat.Data[j1*tmat.Stride+j1:], tmat.Stride) 176 if !isSchurCanonical(a, b, c, d) { 177 t.Errorf("%v: 2×2 block at T[%v,%v] not in Schur canonical form", prefix, j1, j1) 178 } 179 ev1Got, ev2Got := schurBlockEigenvalues(a, b, c, d) 180 181 // Check that the swapped 2×2 block has the same eigenvalues. 182 // The n2×n2 block was originally located at T[j1+n1,j1+n1]. 183 a, b, c, d = extract2x2Block(tmatCopy.Data[(j1+n1)*tmatCopy.Stride+j1+n1:], tmatCopy.Stride) 184 ev1Want, ev2Want := schurBlockEigenvalues(a, b, c, d) 185 if cmplx.Abs(ev1Got-ev1Want) > tol { 186 t.Errorf("%v: unexpected first eigenvalue of 2×2 block at T[%v,%v]. Want %v, got %v", 187 prefix, j1, j1, ev1Want, ev1Got) 188 } 189 if cmplx.Abs(ev2Got-ev2Want) > tol { 190 t.Errorf("%v: unexpected second eigenvalue of 2×2 block at T[%v,%v]. Want %v, got %v", 191 prefix, j1, j1, ev2Want, ev2Got) 192 } 193 } 194 195 if !wantq { 196 return 197 } 198 199 if !isOrthonormal(q) { 200 t.Errorf("%v: Q is not orthogonal", prefix) 201 } 202 // Check that Q is unchanged outside of columns [j1:j1+n1+n2]. 203 for i := 0; i < n; i++ { 204 for j := 0; j < n; j++ { 205 if j1 <= j && j < j1+n1+n2 { 206 continue 207 } 208 diff := q.Data[i*q.Stride+j] - qCopy.Data[i*qCopy.Stride+j] 209 if diff != 0 { 210 t.Errorf("%v: unexpected modification of Q[%v,%v]", prefix, i, j) 211 } 212 } 213 } 214 // Check that Q^T TOrig Q == T. 215 tq := eye(n, n) 216 blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, tmatCopy, q, 0, tq) 217 qtq := eye(n, n) 218 blas64.Gemm(blas.Trans, blas.NoTrans, 1, q, tq, 0, qtq) 219 for i := 0; i < n; i++ { 220 for j := 0; j < n; j++ { 221 diff := qtq.Data[i*qtq.Stride+j] - tmat.Data[i*tmat.Stride+j] 222 if math.Abs(diff) > tol { 223 t.Errorf("%v: unexpected value of T[%v,%v]", prefix, i, j) 224 } 225 } 226 } 227 }