gonum.org/v1/gonum@v0.14.0/lapack/gonum/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 gonum 6 7 import ( 8 "math" 9 10 "gonum.org/v1/gonum/blas/blas64" 11 "gonum.org/v1/gonum/lapack" 12 ) 13 14 // Dgebal balances an n×n matrix A. Balancing consists of two stages, permuting 15 // and scaling. Both steps are optional and depend on the value of job. 16 // 17 // Permuting consists of applying a permutation matrix P such that the matrix 18 // that results from Pᵀ*A*P takes the upper block triangular form 19 // 20 // [ T1 X Y ] 21 // Pᵀ A P = [ 0 B Z ], 22 // [ 0 0 T2 ] 23 // 24 // where T1 and T2 are upper triangular matrices and B contains at least one 25 // nonzero off-diagonal element in each row and column. The indices ilo and ihi 26 // mark the starting and ending columns of the submatrix B. The eigenvalues of A 27 // isolated in the first 0 to ilo-1 and last ihi+1 to n-1 elements on the 28 // diagonal can be read off without any roundoff error. 29 // 30 // Scaling consists of applying a diagonal similarity transformation D such that 31 // D^{-1}*B*D has the 1-norm of each row and its corresponding column nearly 32 // equal. The output matrix is 33 // 34 // [ T1 X*D Y ] 35 // [ 0 inv(D)*B*D inv(D)*Z ]. 36 // [ 0 0 T2 ] 37 // 38 // Scaling may reduce the 1-norm of the matrix, and improve the accuracy of 39 // the computed eigenvalues and/or eigenvectors. 40 // 41 // job specifies the operations that will be performed on A. 42 // If job is lapack.BalanceNone, Dgebal sets scale[i] = 1 for all i and returns ilo=0, ihi=n-1. 43 // If job is lapack.Permute, only permuting will be done. 44 // If job is lapack.Scale, only scaling will be done. 45 // If job is lapack.PermuteScale, both permuting and scaling will be done. 46 // 47 // On return, if job is lapack.Permute or lapack.PermuteScale, it will hold that 48 // 49 // A[i,j] == 0, for i > j and j ∈ {0, ..., ilo-1, ihi+1, ..., n-1}. 50 // 51 // If job is lapack.BalanceNone or lapack.Scale, or if n == 0, it will hold that 52 // 53 // ilo == 0 and ihi == n-1. 54 // 55 // On return, scale will contain information about the permutations and scaling 56 // factors applied to A. If π(j) denotes the index of the column interchanged 57 // with column j, and D[j,j] denotes the scaling factor applied to column j, 58 // then 59 // 60 // scale[j] == π(j), for j ∈ {0, ..., ilo-1, ihi+1, ..., n-1}, 61 // == D[j,j], for j ∈ {ilo, ..., ihi}. 62 // 63 // scale must have length equal to n, otherwise Dgebal will panic. 64 // 65 // Dgebal is an internal routine. It is exported for testing purposes. 66 func (impl Implementation) Dgebal(job lapack.BalanceJob, n int, a []float64, lda int, scale []float64) (ilo, ihi int) { 67 switch { 68 case job != lapack.BalanceNone && job != lapack.Permute && job != lapack.Scale && job != lapack.PermuteScale: 69 panic(badBalanceJob) 70 case n < 0: 71 panic(nLT0) 72 case lda < max(1, n): 73 panic(badLdA) 74 } 75 76 ilo = 0 77 ihi = n - 1 78 79 if n == 0 { 80 return ilo, ihi 81 } 82 83 if len(scale) != n { 84 panic(shortScale) 85 } 86 87 if job == lapack.BalanceNone { 88 for i := range scale { 89 scale[i] = 1 90 } 91 return ilo, ihi 92 } 93 94 if len(a) < (n-1)*lda+n { 95 panic(shortA) 96 } 97 98 bi := blas64.Implementation() 99 swapped := true 100 101 if job == lapack.Scale { 102 goto scaling 103 } 104 105 // Permutation to isolate eigenvalues if possible. 106 // 107 // Search for rows isolating an eigenvalue and push them down. 108 for swapped { 109 swapped = false 110 rows: 111 for i := ihi; i >= 0; i-- { 112 for j := 0; j <= ihi; j++ { 113 if i == j { 114 continue 115 } 116 if a[i*lda+j] != 0 { 117 continue rows 118 } 119 } 120 // Row i has only zero off-diagonal elements in the 121 // block A[ilo:ihi+1,ilo:ihi+1]. 122 scale[ihi] = float64(i) 123 if i != ihi { 124 bi.Dswap(ihi+1, a[i:], lda, a[ihi:], lda) 125 bi.Dswap(n, a[i*lda:], 1, a[ihi*lda:], 1) 126 } 127 if ihi == 0 { 128 scale[0] = 1 129 return ilo, ihi 130 } 131 ihi-- 132 swapped = true 133 break 134 } 135 } 136 // Search for columns isolating an eigenvalue and push them left. 137 swapped = true 138 for swapped { 139 swapped = false 140 columns: 141 for j := ilo; j <= ihi; j++ { 142 for i := ilo; i <= ihi; i++ { 143 if i == j { 144 continue 145 } 146 if a[i*lda+j] != 0 { 147 continue columns 148 } 149 } 150 // Column j has only zero off-diagonal elements in the 151 // block A[ilo:ihi+1,ilo:ihi+1]. 152 scale[ilo] = float64(j) 153 if j != ilo { 154 bi.Dswap(ihi+1, a[j:], lda, a[ilo:], lda) 155 bi.Dswap(n-ilo, a[j*lda+ilo:], 1, a[ilo*lda+ilo:], 1) 156 } 157 swapped = true 158 ilo++ 159 break 160 } 161 } 162 163 scaling: 164 for i := ilo; i <= ihi; i++ { 165 scale[i] = 1 166 } 167 168 if job == lapack.Permute { 169 return ilo, ihi 170 } 171 172 // Balance the submatrix in rows ilo to ihi. 173 174 const ( 175 // sclfac should be a power of 2 to avoid roundoff errors. 176 // Elements of scale are restricted to powers of sclfac, 177 // therefore the matrix will be only nearly balanced. 178 sclfac = 2 179 // factor determines the minimum reduction of the row and column 180 // norms that is considered non-negligible. It must be less than 1. 181 factor = 0.95 182 ) 183 sfmin1 := dlamchS / dlamchP 184 sfmax1 := 1 / sfmin1 185 sfmin2 := sfmin1 * sclfac 186 sfmax2 := 1 / sfmin2 187 188 // Iterative loop for norm reduction. 189 var conv bool 190 for !conv { 191 conv = true 192 for i := ilo; i <= ihi; i++ { 193 c := bi.Dnrm2(ihi-ilo+1, a[ilo*lda+i:], lda) 194 r := bi.Dnrm2(ihi-ilo+1, a[i*lda+ilo:], 1) 195 ica := bi.Idamax(ihi+1, a[i:], lda) 196 ca := math.Abs(a[ica*lda+i]) 197 ira := bi.Idamax(n-ilo, a[i*lda+ilo:], 1) 198 ra := math.Abs(a[i*lda+ilo+ira]) 199 200 // Guard against zero c or r due to underflow. 201 if c == 0 || r == 0 { 202 continue 203 } 204 g := r / sclfac 205 f := 1.0 206 s := c + r 207 for c < g && math.Max(f, math.Max(c, ca)) < sfmax2 && math.Min(r, math.Min(g, ra)) > sfmin2 { 208 if math.IsNaN(c + f + ca + r + g + ra) { 209 // Panic if NaN to avoid infinite loop. 210 panic("lapack: NaN") 211 } 212 f *= sclfac 213 c *= sclfac 214 ca *= sclfac 215 g /= sclfac 216 r /= sclfac 217 ra /= sclfac 218 } 219 g = c / sclfac 220 for r <= g && math.Max(r, ra) < sfmax2 && math.Min(math.Min(f, c), math.Min(g, ca)) > sfmin2 { 221 f /= sclfac 222 c /= sclfac 223 ca /= sclfac 224 g /= sclfac 225 r *= sclfac 226 ra *= sclfac 227 } 228 229 if c+r >= factor*s { 230 // Reduction would be negligible. 231 continue 232 } 233 if f < 1 && scale[i] < 1 && f*scale[i] <= sfmin1 { 234 continue 235 } 236 if f > 1 && scale[i] > 1 && scale[i] >= sfmax1/f { 237 continue 238 } 239 240 // Now balance. 241 scale[i] *= f 242 bi.Dscal(n-ilo, 1/f, a[i*lda+ilo:], 1) 243 bi.Dscal(ihi+1, f, a[i:], lda) 244 conv = false 245 } 246 } 247 return ilo, ihi 248 }