github.com/gonum/lapack@v0.0.0-20181123203213-e4cdc5a0bff9/testlapack/dgehrd.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 Dgehrder interface { 18 Dgehrd(n, ilo, ihi int, a []float64, lda int, tau, work []float64, lwork int) 19 20 Dorgqr(m, n, k int, a []float64, lda int, tau, work []float64, lwork int) 21 } 22 23 func DgehrdTest(t *testing.T, impl Dgehrder) { 24 rnd := rand.New(rand.NewSource(1)) 25 26 // Randomized tests for small matrix sizes that will most likely 27 // use the unblocked algorithm. 28 for _, n := range []int{1, 2, 3, 4, 5, 10, 34} { 29 for _, extra := range []int{0, 13} { 30 for _, optwork := range []bool{true, false} { 31 for cas := 0; cas < 10; cas++ { 32 ilo := rnd.Intn(n) 33 ihi := rnd.Intn(n) 34 if ilo > ihi { 35 ilo, ihi = ihi, ilo 36 } 37 testDgehrd(t, impl, n, ilo, ihi, extra, optwork, rnd) 38 } 39 } 40 } 41 } 42 43 // These are selected tests for larger matrix sizes to test the blocked 44 // algorithm. Use sizes around several powers of two because that is 45 // where the blocked path will most likely start to be taken. For 46 // example, at present the blocked algorithm is used for sizes larger 47 // than 129. 48 for _, test := range []struct { 49 n, ilo, ihi int 50 }{ 51 {0, 0, -1}, 52 53 {68, 0, 63}, 54 {68, 0, 64}, 55 {68, 0, 65}, 56 {68, 0, 66}, 57 {68, 0, 67}, 58 59 {132, 2, 129}, 60 {132, 1, 129}, // Size = 129, unblocked. 61 {132, 0, 129}, // Size = 130, blocked. 62 {132, 1, 130}, 63 {132, 0, 130}, 64 {132, 1, 131}, 65 {132, 0, 131}, 66 67 {260, 2, 257}, 68 {260, 1, 257}, 69 {260, 0, 257}, 70 {260, 0, 258}, 71 {260, 0, 259}, 72 } { 73 for _, extra := range []int{0, 13} { 74 for _, optwork := range []bool{true, false} { 75 testDgehrd(t, impl, test.n, test.ilo, test.ihi, extra, optwork, rnd) 76 } 77 } 78 } 79 } 80 81 func testDgehrd(t *testing.T, impl Dgehrder, n, ilo, ihi, extra int, optwork bool, rnd *rand.Rand) { 82 a := randomGeneral(n, n, n+extra, rnd) 83 aCopy := a 84 aCopy.Data = make([]float64, len(a.Data)) 85 copy(aCopy.Data, a.Data) 86 87 var tau []float64 88 if n > 1 { 89 tau = nanSlice(n - 1) 90 } 91 92 var work []float64 93 if optwork { 94 work = nanSlice(1) 95 impl.Dgehrd(n, ilo, ihi, nil, a.Stride, nil, work, -1) 96 work = nanSlice(int(work[0])) 97 } else { 98 work = nanSlice(max(1, n)) 99 } 100 101 impl.Dgehrd(n, ilo, ihi, a.Data, a.Stride, tau, work, len(work)) 102 103 if n == 0 { 104 // Just make sure there is no panic. 105 return 106 } 107 108 prefix := fmt.Sprintf("Case n=%v, ilo=%v, ihi=%v, extra=%v", n, ilo, ihi, extra) 109 110 // Check any invalid modifications of a. 111 if !generalOutsideAllNaN(a) { 112 t.Errorf("%v: out-of-range write to A\n%v", prefix, a.Data) 113 } 114 for i := ilo; i <= ihi; i++ { 115 for j := 0; j < min(ilo, i); j++ { 116 if a.Data[i*a.Stride+j] != aCopy.Data[i*aCopy.Stride+j] { 117 t.Errorf("%v: unexpected modification of A[%v,%v]", prefix, i, j) 118 } 119 } 120 } 121 for i := ihi + 1; i < n; i++ { 122 for j := 0; j < i; j++ { 123 if a.Data[i*a.Stride+j] != aCopy.Data[i*aCopy.Stride+j] { 124 t.Errorf("%v: unexpected modification of A[%v,%v]", prefix, i, j) 125 } 126 } 127 } 128 for i := 0; i <= ilo; i++ { 129 for j := i; j < ilo+1; j++ { 130 if a.Data[i*a.Stride+j] != aCopy.Data[i*aCopy.Stride+j] { 131 t.Errorf("%v: unexpected modification at A[%v,%v]", prefix, i, j) 132 } 133 } 134 for j := ihi + 1; j < n; j++ { 135 if a.Data[i*a.Stride+j] != aCopy.Data[i*aCopy.Stride+j] { 136 t.Errorf("%v: unexpected modification at A[%v,%v]", prefix, i, j) 137 } 138 } 139 } 140 for i := ihi + 1; i < n; i++ { 141 for j := i; j < n; j++ { 142 if a.Data[i*a.Stride+j] != aCopy.Data[i*aCopy.Stride+j] { 143 t.Errorf("%v: unexpected modification at A[%v,%v]", prefix, i, j) 144 } 145 } 146 } 147 148 // Check that tau has been assigned properly. 149 for i, v := range tau { 150 if math.IsNaN(v) { 151 t.Errorf("%v: unexpected NaN at tau[%v]", prefix, i) 152 } 153 } 154 155 // Extract Q and check that it is orthogonal. 156 q := eye(n, n) 157 if ilo != ihi { 158 for i := ilo + 2; i <= ihi; i++ { 159 for j := ilo + 1; j < ihi; j++ { 160 q.Data[i*q.Stride+j] = a.Data[i*a.Stride+j-1] 161 } 162 } 163 nh := ihi - ilo 164 impl.Dorgqr(nh, nh, nh, q.Data[(ilo+1)*q.Stride+ilo+1:], q.Stride, tau[ilo:ihi], work, len(work)) 165 } 166 if !isOrthonormal(q) { 167 t.Errorf("%v: Q is not orthogonal\nQ=%v", prefix, q) 168 } 169 170 // Construct Q^T * AOrig * Q and check that it is upper Hessenberg. 171 aq := blas64.General{ 172 Rows: n, 173 Cols: n, 174 Stride: n, 175 Data: make([]float64, n*n), 176 } 177 blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, aCopy, q, 0, aq) 178 qaq := blas64.General{ 179 Rows: n, 180 Cols: n, 181 Stride: n, 182 Data: make([]float64, n*n), 183 } 184 blas64.Gemm(blas.Trans, blas.NoTrans, 1, q, aq, 0, qaq) 185 for i := 0; i <= ilo; i++ { 186 for j := ilo + 1; j <= ihi; j++ { 187 qaqij := qaq.Data[i*qaq.Stride+j] 188 diff := qaqij - a.Data[i*a.Stride+j] 189 if math.Abs(diff) > 1e-13 { 190 t.Errorf("%v: Q^T*AOrig*Q and A are not equal, diff at [%v,%v]=%v", prefix, i, j, diff) 191 } 192 } 193 } 194 for i := ilo + 1; i <= ihi; i++ { 195 for j := ilo; j < n; j++ { 196 qaqij := qaq.Data[i*qaq.Stride+j] 197 if j < i-1 { 198 if math.Abs(qaqij) > 1e-13 { 199 t.Errorf("%v: Q^T*AOrig*Q is not upper Hessenberg, [%v,%v]=%v", prefix, i, j, qaqij) 200 } 201 continue 202 } 203 diff := qaqij - a.Data[i*a.Stride+j] 204 if math.Abs(diff) > 1e-13 { 205 t.Errorf("%v: Q^T*AOrig*Q and A are not equal, diff at [%v,%v]=%v", prefix, i, j, diff) 206 } 207 } 208 } 209 }