github.com/gonum/lapack@v0.0.0-20181123203213-e4cdc5a0bff9/testlapack/dgebal.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/rand" 10 "testing" 11 12 "github.com/gonum/blas" 13 "github.com/gonum/blas/blas64" 14 "github.com/gonum/lapack" 15 ) 16 17 type Dgebaler interface { 18 Dgebal(job lapack.Job, n int, a []float64, lda int, scale []float64) (int, int) 19 } 20 21 func DgebalTest(t *testing.T, impl Dgebaler) { 22 rnd := rand.New(rand.NewSource(1)) 23 24 for _, job := range []lapack.Job{lapack.None, lapack.Permute, lapack.Scale, lapack.PermuteScale} { 25 for _, n := range []int{0, 1, 2, 3, 4, 5, 6, 10, 18, 31, 53, 100} { 26 for _, extra := range []int{0, 11} { 27 for cas := 0; cas < 100; cas++ { 28 a := unbalancedSparseGeneral(n, n, n+extra, 2*n, rnd) 29 testDgebal(t, impl, job, a) 30 } 31 } 32 } 33 } 34 } 35 36 func testDgebal(t *testing.T, impl Dgebaler, job lapack.Job, a blas64.General) { 37 const tol = 1e-14 38 39 n := a.Rows 40 extra := a.Stride - n 41 42 var scale []float64 43 if n > 0 { 44 scale = nanSlice(n) 45 } 46 47 want := cloneGeneral(a) 48 49 ilo, ihi := impl.Dgebal(job, n, a.Data, a.Stride, scale) 50 51 prefix := fmt.Sprintf("Case job=%v, n=%v, extra=%v", job, n, extra) 52 53 if !generalOutsideAllNaN(a) { 54 t.Errorf("%v: out-of-range write to A\n%v", prefix, a.Data) 55 } 56 57 if n == 0 { 58 if ilo != 0 { 59 t.Errorf("%v: unexpected ilo when n=0. Want 0, got %v", prefix, n, ilo) 60 } 61 if ihi != -1 { 62 t.Errorf("%v: unexpected ihi when n=0. Want -1, got %v", prefix, n, ihi) 63 } 64 return 65 } 66 67 if job == lapack.None { 68 if ilo != 0 { 69 t.Errorf("%v: unexpected ilo when job=None. Want 0, got %v", prefix, ilo) 70 } 71 if ihi != n-1 { 72 t.Errorf("%v: unexpected ihi when job=None. Want %v, got %v", prefix, n-1, ihi) 73 } 74 k := -1 75 for i := range scale { 76 if scale[i] != 1 { 77 k = i 78 break 79 } 80 } 81 if k != -1 { 82 t.Errorf("%v: unexpected scale[%v] when job=None. Want 1, got %v", prefix, k, scale[k]) 83 } 84 if !equalApproxGeneral(a, want, 0) { 85 t.Errorf("%v: unexpected modification of A when job=None", prefix) 86 } 87 return 88 } 89 90 if ilo < 0 || ihi < ilo || n <= ihi { 91 t.Errorf("%v: invalid ordering of ilo=%v and ihi=%v", prefix, ilo, ihi) 92 } 93 94 if ilo >= 2 && !isUpperTriangular(blas64.General{ilo - 1, ilo - 1, a.Stride, a.Data}) { 95 t.Errorf("%v: T1 is not upper triangular", prefix) 96 } 97 m := n - ihi - 1 // Order of T2. 98 k := ihi + 1 99 if m >= 2 && !isUpperTriangular(blas64.General{m, m, a.Stride, a.Data[k*a.Stride+k:]}) { 100 t.Errorf("%v: T2 is not upper triangular", prefix) 101 } 102 103 if job == lapack.Permute || job == lapack.PermuteScale { 104 // Check that all rows in [ilo:ihi+1] have at least one nonzero 105 // off-diagonal element. 106 zeroRow := -1 107 for i := ilo; i <= ihi; i++ { 108 onlyZeros := true 109 for j := ilo; j <= ihi; j++ { 110 if i != j && a.Data[i*a.Stride+j] != 0 { 111 onlyZeros = false 112 break 113 } 114 } 115 if onlyZeros { 116 zeroRow = i 117 break 118 } 119 } 120 if zeroRow != -1 && ilo != ihi { 121 t.Errorf("%v: row %v has only zero off-diagonal elements, ilo=%v, ihi=%v", prefix, zeroRow, ilo, ihi) 122 } 123 // Check that all columns in [ilo:ihi+1] have at least one nonzero 124 // off-diagonal element. 125 zeroCol := -1 126 for j := ilo; j <= ihi; j++ { 127 onlyZeros := true 128 for i := ilo; i <= ihi; i++ { 129 if i != j && a.Data[i*a.Stride+j] != 0 { 130 onlyZeros = false 131 break 132 } 133 } 134 if onlyZeros { 135 zeroCol = j 136 break 137 } 138 } 139 if zeroCol != -1 && ilo != ihi { 140 t.Errorf("%v: column %v has only zero off-diagonal elements, ilo=%v, ihi=%v", prefix, zeroCol, ilo, ihi) 141 } 142 143 // Create the permutation matrix P. 144 p := eye(n, n) 145 for j := n - 1; j > ihi; j-- { 146 blas64.Swap(n, 147 blas64.Vector{p.Stride, p.Data[j:]}, 148 blas64.Vector{p.Stride, p.Data[int(scale[j]):]}) 149 } 150 for j := 0; j < ilo; j++ { 151 blas64.Swap(n, 152 blas64.Vector{p.Stride, p.Data[j:]}, 153 blas64.Vector{p.Stride, p.Data[int(scale[j]):]}) 154 } 155 // Compute P^T*A*P and store into want. 156 ap := zeros(n, n, n) 157 blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, want, p, 0, ap) 158 blas64.Gemm(blas.Trans, blas.NoTrans, 1, p, ap, 0, want) 159 } 160 if job == lapack.Scale || job == lapack.PermuteScale { 161 // Modify want by D and D^{-1}. 162 d := eye(n, n) 163 dinv := eye(n, n) 164 for i := ilo; i <= ihi; i++ { 165 d.Data[i*d.Stride+i] = scale[i] 166 dinv.Data[i*dinv.Stride+i] = 1 / scale[i] 167 } 168 ad := zeros(n, n, n) 169 blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, want, d, 0, ad) 170 blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, dinv, ad, 0, want) 171 } 172 if !equalApproxGeneral(want, a, tol) { 173 t.Errorf("%v: unexpected value of A, ilo=%v, ihi=%v", prefix, ilo, ihi) 174 } 175 }