gonum.org/v1/gonum@v0.14.0/lapack/testlapack/dlaqr23.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 "testing" 10 11 "golang.org/x/exp/rand" 12 13 "gonum.org/v1/gonum/blas" 14 "gonum.org/v1/gonum/blas/blas64" 15 "gonum.org/v1/gonum/lapack" 16 ) 17 18 type Dlaqr23er interface { 19 Dlaqr23(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, recur int) (ns, nd int) 20 } 21 22 type dlaqr23Test struct { 23 wantt, wantz bool 24 ktop, kbot int 25 nw int 26 h blas64.General 27 iloz, ihiz int 28 29 evWant []complex128 // Optional slice with known eigenvalues. 30 } 31 32 func newDlaqr23TestCase(wantt, wantz bool, n, ldh int, rnd *rand.Rand) dlaqr23Test { 33 // Generate the deflation window size. 34 var nw int 35 if n <= 75 { 36 // For small matrices any window size works because they will 37 // always use Dlahrq inside Dlaqr23. 38 nw = rnd.Intn(n) + 1 39 } else { 40 // For sufficiently large matrices generate a large enough 41 // window to assure that the Dlaqr4 path is taken. 42 nw = 76 + rnd.Intn(n-75) 43 } 44 // Generate a random Hessenberg matrix. 45 h := randomHessenberg(n, ldh, rnd) 46 // Generate the block limits of H on which Dlaqr23 will operate so that 47 // the restriction 48 // 0 <= nw <= kbot-ktop+1 49 // is satisfied. 50 ktop := rnd.Intn(n - nw + 1) 51 kbot := ktop + nw - 1 52 kbot += rnd.Intn(n - kbot) 53 // Make the block isolated by zeroing out the sub-diagonal elements. 54 if ktop-1 >= 0 { 55 h.Data[ktop*h.Stride+ktop-1] = 0 56 } 57 if kbot+1 < n { 58 h.Data[(kbot+1)*h.Stride+kbot] = 0 59 } 60 // Generate the rows of Z to which transformations will be applied if 61 // wantz is true. 62 iloz := rnd.Intn(ktop + 1) 63 ihiz := kbot + rnd.Intn(n-kbot) 64 return dlaqr23Test{ 65 wantt: wantt, 66 wantz: wantz, 67 ktop: ktop, 68 kbot: kbot, 69 nw: nw, 70 h: h, 71 iloz: iloz, 72 ihiz: ihiz, 73 } 74 } 75 76 func Dlaqr23Test(t *testing.T, impl Dlaqr23er) { 77 rnd := rand.New(rand.NewSource(1)) 78 79 // Randomized tests. 80 for _, wantt := range []bool{true, false} { 81 for _, wantz := range []bool{true, false} { 82 for _, n := range []int{1, 2, 3, 4, 5, 6, 10, 18, 31, 100} { 83 for _, extra := range []int{0, 11} { 84 for cas := 0; cas < 10; cas++ { 85 test := newDlaqr23TestCase(wantt, wantz, n, n+extra, rnd) 86 testDlaqr23(t, impl, test, false, 1, rnd) 87 testDlaqr23(t, impl, test, true, 1, rnd) 88 testDlaqr23(t, impl, test, false, 0, rnd) 89 testDlaqr23(t, impl, test, true, 0, rnd) 90 } 91 } 92 } 93 } 94 } 95 96 // Tests with n=0. 97 for _, wantt := range []bool{true, false} { 98 for _, wantz := range []bool{true, false} { 99 for _, extra := range []int{0, 1, 11} { 100 test := dlaqr23Test{ 101 wantt: wantt, 102 wantz: wantz, 103 h: randomHessenberg(0, extra, rnd), 104 ktop: 0, 105 kbot: -1, 106 iloz: 0, 107 ihiz: -1, 108 nw: 0, 109 } 110 testDlaqr23(t, impl, test, true, 1, rnd) 111 testDlaqr23(t, impl, test, false, 1, rnd) 112 testDlaqr23(t, impl, test, true, 0, rnd) 113 testDlaqr23(t, impl, test, false, 0, rnd) 114 } 115 } 116 } 117 118 // Tests with explicit eigenvalues computed by Octave. 119 for _, test := range []dlaqr23Test{ 120 { 121 h: blas64.General{ 122 Rows: 1, 123 Cols: 1, 124 Stride: 1, 125 Data: []float64{7.09965484086874e-1}, 126 }, 127 ktop: 0, 128 kbot: 0, 129 iloz: 0, 130 ihiz: 0, 131 evWant: []complex128{7.09965484086874e-1}, 132 }, 133 { 134 h: blas64.General{ 135 Rows: 2, 136 Cols: 2, 137 Stride: 2, 138 Data: []float64{ 139 0, -1, 140 1, 0, 141 }, 142 }, 143 ktop: 0, 144 kbot: 1, 145 iloz: 0, 146 ihiz: 1, 147 evWant: []complex128{1i, -1i}, 148 }, 149 { 150 h: blas64.General{ 151 Rows: 2, 152 Cols: 2, 153 Stride: 2, 154 Data: []float64{ 155 6.25219991450918e-1, 8.17510791994361e-1, 156 3.31218891622294e-1, 1.24103744878131e-1, 157 }, 158 }, 159 ktop: 0, 160 kbot: 1, 161 iloz: 0, 162 ihiz: 1, 163 evWant: []complex128{9.52203547663447e-1, -2.02879811334398e-1}, 164 }, 165 { 166 h: blas64.General{ 167 Rows: 4, 168 Cols: 4, 169 Stride: 4, 170 Data: []float64{ 171 1, 0, 0, 0, 172 0, 6.25219991450918e-1, 8.17510791994361e-1, 0, 173 0, 3.31218891622294e-1, 1.24103744878131e-1, 0, 174 0, 0, 0, 1, 175 }, 176 }, 177 ktop: 1, 178 kbot: 2, 179 iloz: 0, 180 ihiz: 3, 181 evWant: []complex128{9.52203547663447e-1, -2.02879811334398e-1}, 182 }, 183 { 184 h: blas64.General{ 185 Rows: 2, 186 Cols: 2, 187 Stride: 2, 188 Data: []float64{ 189 -1.1219562276608, 6.85473513349362e-1, 190 -8.19951061145131e-1, 1.93728523178888e-1, 191 }, 192 }, 193 ktop: 0, 194 kbot: 1, 195 iloz: 0, 196 ihiz: 1, 197 evWant: []complex128{ 198 -4.64113852240958e-1 + 3.59580510817350e-1i, 199 -4.64113852240958e-1 - 3.59580510817350e-1i, 200 }, 201 }, 202 { 203 h: blas64.General{ 204 Rows: 5, 205 Cols: 5, 206 Stride: 5, 207 Data: []float64{ 208 9.57590178533658e-1, -5.10651295522708e-1, 9.24974510015869e-1, -1.30016306879522e-1, 2.92601986926954e-2, 209 -1.08084756637964, 1.77529701001213, -1.36480197632509, 2.23196371219601e-1, 1.12912853063308e-1, 210 0, -8.44075612174676e-1, 1.067867614486, -2.55782915176399e-1, -2.00598563137468e-1, 211 0, 0, -5.67097237165410e-1, 2.07205057427341e-1, 6.54998340743380e-1, 212 0, 0, 0, -1.89441413886041e-1, -4.18125416021786e-1, 213 }, 214 }, 215 ktop: 0, 216 kbot: 4, 217 iloz: 0, 218 ihiz: 4, 219 evWant: []complex128{ 220 2.94393309555622, 221 4.97029793606701e-1 + 3.63041654992384e-1i, 222 4.97029793606701e-1 - 3.63041654992384e-1i, 223 -1.74079119166145e-1 + 2.01570009462092e-1i, 224 -1.74079119166145e-1 - 2.01570009462092e-1i, 225 }, 226 }, 227 } { 228 test.wantt = true 229 test.wantz = true 230 test.nw = test.kbot - test.ktop + 1 231 testDlaqr23(t, impl, test, true, 1, rnd) 232 testDlaqr23(t, impl, test, false, 1, rnd) 233 testDlaqr23(t, impl, test, true, 0, rnd) 234 testDlaqr23(t, impl, test, false, 0, rnd) 235 } 236 } 237 238 func testDlaqr23(t *testing.T, impl Dlaqr23er, test dlaqr23Test, opt bool, recur int, rnd *rand.Rand) { 239 const tol = 1e-14 240 241 // Clone the test matrix to avoid modifying test data. 242 h := cloneGeneral(test.h) 243 // Extract test values to simplify notation. 244 n := h.Cols 245 extra := h.Stride - h.Cols 246 wantt := test.wantt 247 wantz := test.wantz 248 ktop := test.ktop 249 kbot := test.kbot 250 nw := test.nw 251 iloz := test.iloz 252 ihiz := test.ihiz 253 254 var z, zCopy blas64.General 255 if wantz { 256 // Using the identity matrix for Z is the easiest way to check 257 // that the transformation accumulated into it by Dlaqr23 is orthogonal. 258 z = eye(n, n+extra) 259 zCopy = cloneGeneral(z) 260 } 261 262 // Allocate slices for storing the converged eigenvalues, initially 263 // filled with NaN. 264 sr := nanSlice(kbot + 1) 265 si := nanSlice(kbot + 1) 266 267 // Allocate work matrices. 268 v := randomGeneral(nw, nw, nw+extra, rnd) 269 var nh int 270 if nw > 0 { 271 nh = nw + rnd.Intn(nw) // nh must be at least nw. 272 } 273 tmat := randomGeneral(nw, nh, nh+extra, rnd) 274 var nv int 275 if nw > 0 { 276 nv = rnd.Intn(nw) + 1 277 } 278 wv := randomGeneral(nv, nw, nw+extra, rnd) 279 280 var work []float64 281 if opt { 282 // Allocate work slice with optimal length. 283 work = nanSlice(1) 284 impl.Dlaqr23(wantt, wantz, n, ktop, kbot, nw, h.Data, h.Stride, iloz, ihiz, z.Data, max(1, z.Stride), 285 sr, si, v.Data, v.Stride, tmat.Cols, tmat.Data, tmat.Stride, wv.Rows, wv.Data, wv.Stride, work, -1, recur) 286 work = nanSlice(int(work[0])) 287 } else { 288 // Allocate work slice with minimum length. 289 work = nanSlice(max(1, 2*nw)) 290 } 291 292 ns, nd := impl.Dlaqr23(wantt, wantz, n, ktop, kbot, nw, h.Data, h.Stride, iloz, ihiz, z.Data, max(1, z.Stride), 293 sr, si, v.Data, v.Stride, tmat.Cols, tmat.Data, tmat.Stride, wv.Rows, wv.Data, wv.Stride, work, len(work), recur) 294 295 prefix := fmt.Sprintf("Case wantt=%v, wantz=%v, n=%v, ktop=%v, kbot=%v, nw=%v, iloz=%v, ihiz=%v, extra=%v", 296 wantt, wantz, n, ktop, kbot, nw, iloz, ihiz, extra) 297 298 if !generalOutsideAllNaN(h) { 299 t.Errorf("%v: out-of-range write to H\n%v", prefix, h.Data) 300 } 301 if !generalOutsideAllNaN(z) { 302 t.Errorf("%v: out-of-range write to Z\n%v", prefix, z.Data) 303 } 304 if !generalOutsideAllNaN(v) { 305 t.Errorf("%v: out-of-range write to V\n%v", prefix, v.Data) 306 } 307 if !generalOutsideAllNaN(tmat) { 308 t.Errorf("%v: out-of-range write to T\n%v", prefix, tmat.Data) 309 } 310 if !generalOutsideAllNaN(wv) { 311 t.Errorf("%v: out-of-range write to WV\n%v", prefix, wv.Data) 312 } 313 if !isAllNaN(sr[:kbot-nd-ns+1]) || !isAllNaN(sr[kbot+1:]) { 314 t.Errorf("%v: out-of-range write to sr", prefix) 315 } 316 if !isAllNaN(si[:kbot-nd-ns+1]) || !isAllNaN(si[kbot+1:]) { 317 t.Errorf("%v: out-of-range write to si", prefix) 318 } 319 320 if !isUpperHessenberg(h) { 321 t.Errorf("%v: H is not upper Hessenberg", prefix) 322 } 323 324 if test.evWant != nil { 325 // Check all converged eigenvalues against known eigenvalues. 326 for i := kbot - nd + 1; i <= kbot; i++ { 327 ev := complex(sr[i], si[i]) 328 found, _ := containsComplex(test.evWant, ev, tol) 329 if !found { 330 t.Errorf("%v: unexpected eigenvalue %v", prefix, ev) 331 } 332 } 333 } 334 335 // Checks below need the matrix Z. 336 if !wantz { 337 return 338 } 339 340 // Test whether the matrix Z was modified outside the given block. 341 var zmod bool 342 for i := 0; i < n; i++ { 343 for j := 0; j < n; j++ { 344 if z.Data[i*z.Stride+j] == zCopy.Data[i*zCopy.Stride+j] { 345 continue 346 } 347 if i < iloz || ihiz < i || j < kbot-nw+1 || kbot < j { 348 zmod = true 349 } 350 } 351 } 352 if zmod { 353 t.Errorf("%v: unexpected modification of Z", prefix) 354 } 355 // Check that Z is orthogonal. 356 if resid := residualOrthogonal(z, false); resid > tol*float64(n) { 357 t.Errorf("Case %v: Z is not orthogonal; resid=%v, want<=%v", prefix, resid, tol*float64(n)) 358 } 359 if wantt { 360 // Check that |Zᵀ*HOrig*Z - H| is small where H is the result from Dlaqr23. 361 hz := zeros(n, n, n) 362 blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, test.h, z, 0, hz) 363 r := cloneGeneral(h) 364 blas64.Gemm(blas.Trans, blas.NoTrans, 1, z, hz, -1, r) 365 resid := dlange(lapack.MaxColumnSum, r.Rows, r.Cols, r.Data, r.Stride) 366 if resid > tol*float64(n) { 367 t.Errorf("%v: |Zᵀ*(initial H)*Z - (final H)|=%v, want<=%v", prefix, resid, tol*float64(n)) 368 } 369 } 370 }