github.com/gonum/lapack@v0.0.0-20181123203213-e4cdc5a0bff9/testlapack/dlaqr04.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/rand" 11 "testing" 12 13 "github.com/gonum/blas" 14 "github.com/gonum/blas/blas64" 15 ) 16 17 type Dlaqr04er interface { 18 Dlaqr04(wantt, wantz bool, n, ilo, ihi int, h []float64, ldh int, wr, wi []float64, iloz, ihiz int, z []float64, ldz int, work []float64, lwork int, recur int) int 19 20 Dlahqrer 21 } 22 23 type dlaqr04Test struct { 24 h blas64.General 25 ilo, ihi int 26 iloz, ihiz int 27 wantt, wantz bool 28 29 evWant []complex128 // Optional slice holding known eigenvalues. 30 } 31 32 func Dlaqr04Test(t *testing.T, impl Dlaqr04er) { 33 rnd := rand.New(rand.NewSource(1)) 34 35 // Tests for small matrices that choose the ilo,ihi and iloz,ihiz pairs 36 // randomly. 37 for _, wantt := range []bool{true, false} { 38 for _, wantz := range []bool{true, false} { 39 for _, n := range []int{1, 2, 3, 4, 5, 6, 10, 11, 12, 18, 29} { 40 for _, extra := range []int{0, 11} { 41 for recur := 0; recur <= 2; recur++ { 42 for cas := 0; cas < n; cas++ { 43 ilo := rnd.Intn(n) 44 ihi := rnd.Intn(n) 45 if ilo > ihi { 46 ilo, ihi = ihi, ilo 47 } 48 iloz := rnd.Intn(ilo + 1) 49 ihiz := ihi + rnd.Intn(n-ihi) 50 h := randomHessenberg(n, n+extra, rnd) 51 if ilo-1 >= 0 { 52 h.Data[ilo*h.Stride+ilo-1] = 0 53 } 54 if ihi+1 < n { 55 h.Data[(ihi+1)*h.Stride+ihi] = 0 56 } 57 test := dlaqr04Test{ 58 h: h, 59 ilo: ilo, 60 ihi: ihi, 61 iloz: iloz, 62 ihiz: ihiz, 63 wantt: wantt, 64 wantz: wantz, 65 } 66 testDlaqr04(t, impl, test, false, recur) 67 testDlaqr04(t, impl, test, true, recur) 68 } 69 } 70 } 71 } 72 } 73 } 74 75 // Tests for matrices large enough to possibly use the recursion (but it 76 // doesn't seem to be the case). 77 for _, n := range []int{100, 500} { 78 for cas := 0; cas < 5; cas++ { 79 h := randomHessenberg(n, n, rnd) 80 test := dlaqr04Test{ 81 h: h, 82 ilo: 0, 83 ihi: n - 1, 84 iloz: 0, 85 ihiz: n - 1, 86 wantt: true, 87 wantz: true, 88 } 89 testDlaqr04(t, impl, test, true, 1) 90 } 91 } 92 93 // Tests that make sure that some potentially problematic corner cases, 94 // like zero-sized matrix, are covered. 95 for _, wantt := range []bool{true, false} { 96 for _, wantz := range []bool{true, false} { 97 for _, extra := range []int{0, 1, 11} { 98 for _, test := range []dlaqr04Test{ 99 { 100 h: randomHessenberg(0, extra, rnd), 101 ilo: 0, 102 ihi: -1, 103 iloz: 0, 104 ihiz: -1, 105 }, 106 { 107 h: randomHessenberg(1, 1+extra, rnd), 108 ilo: 0, 109 ihi: 0, 110 iloz: 0, 111 ihiz: 0, 112 }, 113 { 114 h: randomHessenberg(2, 2+extra, rnd), 115 ilo: 1, 116 ihi: 1, 117 iloz: 1, 118 ihiz: 1, 119 }, 120 { 121 h: randomHessenberg(2, 2+extra, rnd), 122 ilo: 0, 123 ihi: 1, 124 iloz: 0, 125 ihiz: 1, 126 }, 127 { 128 h: randomHessenberg(10, 10+extra, rnd), 129 ilo: 0, 130 ihi: 0, 131 iloz: 0, 132 ihiz: 0, 133 }, 134 { 135 h: randomHessenberg(10, 10+extra, rnd), 136 ilo: 0, 137 ihi: 9, 138 iloz: 0, 139 ihiz: 9, 140 }, 141 { 142 h: randomHessenberg(10, 10+extra, rnd), 143 ilo: 0, 144 ihi: 1, 145 iloz: 0, 146 ihiz: 1, 147 }, 148 { 149 h: randomHessenberg(10, 10+extra, rnd), 150 ilo: 0, 151 ihi: 1, 152 iloz: 0, 153 ihiz: 9, 154 }, 155 { 156 h: randomHessenberg(10, 10+extra, rnd), 157 ilo: 9, 158 ihi: 9, 159 iloz: 0, 160 ihiz: 9, 161 }, 162 } { 163 if test.ilo-1 >= 0 { 164 test.h.Data[test.ilo*test.h.Stride+test.ilo-1] = 0 165 } 166 if test.ihi+1 < test.h.Rows { 167 test.h.Data[(test.ihi+1)*test.h.Stride+test.ihi] = 0 168 } 169 test.wantt = wantt 170 test.wantz = wantz 171 testDlaqr04(t, impl, test, false, 1) 172 testDlaqr04(t, impl, test, true, 1) 173 } 174 } 175 } 176 } 177 178 // Tests with known eigenvalues computed by Octave. 179 for _, test := range []dlaqr04Test{ 180 { 181 h: blas64.General{ 182 Rows: 1, 183 Cols: 1, 184 Stride: 1, 185 Data: []float64{7.09965484086874e-1}, 186 }, 187 ilo: 0, 188 ihi: 0, 189 iloz: 0, 190 ihiz: 0, 191 evWant: []complex128{7.09965484086874e-1}, 192 }, 193 { 194 h: blas64.General{ 195 Rows: 2, 196 Cols: 2, 197 Stride: 2, 198 Data: []float64{ 199 0, -1, 200 1, 0, 201 }, 202 }, 203 ilo: 0, 204 ihi: 1, 205 iloz: 0, 206 ihiz: 1, 207 evWant: []complex128{1i, -1i}, 208 }, 209 { 210 h: blas64.General{ 211 Rows: 2, 212 Cols: 2, 213 Stride: 2, 214 Data: []float64{ 215 6.25219991450918e-1, 8.17510791994361e-1, 216 3.31218891622294e-1, 1.24103744878131e-1, 217 }, 218 }, 219 ilo: 0, 220 ihi: 1, 221 iloz: 0, 222 ihiz: 1, 223 evWant: []complex128{9.52203547663447e-1, -2.02879811334398e-1}, 224 }, 225 { 226 h: blas64.General{ 227 Rows: 4, 228 Cols: 4, 229 Stride: 4, 230 Data: []float64{ 231 1, 0, 0, 0, 232 0, 6.25219991450918e-1, 8.17510791994361e-1, 0, 233 0, 3.31218891622294e-1, 1.24103744878131e-1, 0, 234 0, 0, 0, 1, 235 }, 236 }, 237 ilo: 1, 238 ihi: 2, 239 iloz: 0, 240 ihiz: 3, 241 evWant: []complex128{9.52203547663447e-1, -2.02879811334398e-1}, 242 }, 243 { 244 h: blas64.General{ 245 Rows: 2, 246 Cols: 2, 247 Stride: 2, 248 Data: []float64{ 249 -1.1219562276608, 6.85473513349362e-1, 250 -8.19951061145131e-1, 1.93728523178888e-1, 251 }, 252 }, 253 ilo: 0, 254 ihi: 1, 255 iloz: 0, 256 ihiz: 1, 257 evWant: []complex128{ 258 -4.64113852240958e-1 + 3.59580510817350e-1i, 259 -4.64113852240958e-1 - 3.59580510817350e-1i, 260 }, 261 }, 262 { 263 h: blas64.General{ 264 Rows: 5, 265 Cols: 5, 266 Stride: 5, 267 Data: []float64{ 268 9.57590178533658e-1, -5.10651295522708e-1, 9.24974510015869e-1, -1.30016306879522e-1, 2.92601986926954e-2, 269 -1.08084756637964, 1.77529701001213, -1.36480197632509, 2.23196371219601e-1, 1.12912853063308e-1, 270 0, -8.44075612174676e-1, 1.067867614486, -2.55782915176399e-1, -2.00598563137468e-1, 271 0, 0, -5.67097237165410e-1, 2.07205057427341e-1, 6.54998340743380e-1, 272 0, 0, 0, -1.89441413886041e-1, -4.18125416021786e-1, 273 }, 274 }, 275 ilo: 0, 276 ihi: 4, 277 iloz: 0, 278 ihiz: 4, 279 evWant: []complex128{ 280 2.94393309555622, 281 4.97029793606701e-1 + 3.63041654992384e-1i, 282 4.97029793606701e-1 - 3.63041654992384e-1i, 283 -1.74079119166145e-1 + 2.01570009462092e-1i, 284 -1.74079119166145e-1 - 2.01570009462092e-1i, 285 }, 286 }, 287 } { 288 test.wantt = true 289 test.wantz = true 290 testDlaqr04(t, impl, test, false, 1) 291 testDlaqr04(t, impl, test, true, 1) 292 } 293 } 294 295 func testDlaqr04(t *testing.T, impl Dlaqr04er, test dlaqr04Test, optwork bool, recur int) { 296 const tol = 1e-14 297 298 h := cloneGeneral(test.h) 299 n := h.Cols 300 extra := h.Stride - h.Cols 301 wantt := test.wantt 302 wantz := test.wantz 303 ilo := test.ilo 304 ihi := test.ihi 305 iloz := test.iloz 306 ihiz := test.ihiz 307 308 var z, zCopy blas64.General 309 if wantz { 310 z = eye(n, n+extra) 311 zCopy = cloneGeneral(z) 312 } 313 314 wr := nanSlice(ihi + 1) 315 wi := nanSlice(ihi + 1) 316 317 var work []float64 318 if optwork { 319 work = nanSlice(1) 320 impl.Dlaqr04(wantt, wantz, n, ilo, ihi, nil, 0, nil, nil, iloz, ihiz, nil, 0, work, -1, recur) 321 work = nanSlice(int(work[0])) 322 } else { 323 work = nanSlice(max(1, n)) 324 } 325 326 unconverged := impl.Dlaqr04(wantt, wantz, n, ilo, ihi, h.Data, h.Stride, wr, wi, iloz, ihiz, z.Data, z.Stride, work, len(work), recur) 327 328 prefix := fmt.Sprintf("Case wantt=%v, wantz=%v, n=%v, ilo=%v, ihi=%v, iloz=%v, ihiz=%v, extra=%v, opt=%v", 329 wantt, wantz, n, ilo, ihi, iloz, ihiz, extra, optwork) 330 331 if !generalOutsideAllNaN(h) { 332 t.Errorf("%v: out-of-range write to H\n%v", prefix, h.Data) 333 } 334 if !generalOutsideAllNaN(z) { 335 t.Errorf("%v: out-of-range write to Z\n%v", prefix, z.Data) 336 } 337 338 start := ilo // Index of the first computed eigenvalue. 339 if unconverged != 0 { 340 start = unconverged 341 if start == ihi+1 { 342 t.Logf("%v: no eigenvalue has converged", prefix) 343 } 344 } 345 346 // Check that wr and wi have not been modified within [:start]. 347 if !isAllNaN(wr[:start]) { 348 t.Errorf("%v: unexpected modification of wr", prefix) 349 } 350 if !isAllNaN(wi[:start]) { 351 t.Errorf("%v: unexpected modification of wi", prefix) 352 } 353 354 var hasReal bool 355 for i := start; i <= ihi; { 356 if wi[i] == 0 { // Real eigenvalue. 357 hasReal = true 358 // Check that the eigenvalue corresponds to a 1×1 block 359 // on the diagonal of H. 360 if wantt && wr[i] != h.Data[i*h.Stride+i] { 361 t.Errorf("%v: wr[%v] != H[%v,%v]", prefix, i, i, i) 362 } 363 i++ 364 continue 365 } 366 367 // Complex eigenvalue. 368 369 // In the conjugate pair the real parts must be equal. 370 if wr[i] != wr[i+1] { 371 t.Errorf("%v: real part of conjugate pair not equal, i=%v", prefix, i) 372 } 373 // The first imaginary part must be positive. 374 if wi[i] < 0 { 375 t.Errorf("%v: wi[%v] not positive", prefix, i) 376 } 377 // The second imaginary part must be negative with the same 378 // magnitude. 379 if wi[i] != -wi[i+1] { 380 t.Errorf("%v: wi[%v] != wi[%v]", prefix, i, i+1) 381 } 382 if wantt { 383 // Check that wi[i] has the correct value. 384 if wr[i] != h.Data[i*h.Stride+i] { 385 t.Errorf("%v: wr[%v] != H[%v,%v]", prefix, i, i, i) 386 } 387 if wr[i] != h.Data[(i+1)*h.Stride+i+1] { 388 t.Errorf("%v: wr[%v] != H[%v,%v]", prefix, i, i+1, i+1) 389 } 390 im := math.Sqrt(math.Abs(h.Data[(i+1)*h.Stride+i])) * math.Sqrt(math.Abs(h.Data[i*h.Stride+i+1])) 391 if math.Abs(im-wi[i]) > tol { 392 t.Errorf("%v: unexpected value of wi[%v]: want %v, got %v", prefix, i, im, wi[i]) 393 } 394 } 395 i += 2 396 } 397 // If the number of found eigenvalues is odd, at least one must be real. 398 if (ihi+1-start)%2 != 0 && !hasReal { 399 t.Errorf("%v: expected at least one real eigenvalue", prefix) 400 } 401 402 // Compare found eigenvalues to the reference, if known. 403 if test.evWant != nil { 404 for i := start; i <= ihi; i++ { 405 ev := complex(wr[i], wi[i]) 406 found, _ := containsComplex(test.evWant, ev, tol) 407 if !found { 408 t.Errorf("%v: unexpected eigenvalue %v", prefix, ev) 409 } 410 } 411 } 412 413 if !wantz { 414 return 415 } 416 417 // Z should contain the orthogonal matrix U. 418 if !isOrthonormal(z) { 419 t.Errorf("%v: Z is not orthogonal", prefix) 420 } 421 // Z should have been modified only in the 422 // [iloz:ihiz+1,ilo:ihi+1] block. 423 for i := 0; i < n; i++ { 424 for j := 0; j < n; j++ { 425 if iloz <= i && i <= ihiz && ilo <= j && j <= ihi { 426 continue 427 } 428 if z.Data[i*z.Stride+j] != zCopy.Data[i*zCopy.Stride+j] { 429 t.Errorf("%v: Z modified outside of [iloz:ihiz+1,ilo:ihi+1] block", prefix) 430 } 431 } 432 } 433 if wantt { 434 // Zero out h under the subdiagonal because Dlaqr04 uses it as 435 // workspace. 436 for i := 2; i < n; i++ { 437 for j := 0; j < i-1; j++ { 438 h.Data[i*h.Stride+j] = 0 439 } 440 } 441 hz := eye(n, n) 442 blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, test.h, z, 0, hz) 443 zhz := eye(n, n) 444 blas64.Gemm(blas.Trans, blas.NoTrans, 1, z, hz, 0, zhz) 445 if !equalApproxGeneral(zhz, h, 10*tol) { 446 t.Errorf("%v: Z^T*(initial H)*Z and (final H) are not equal", prefix) 447 } 448 } 449 }