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.
10 "gonum.org/v1/gonum/blas/blas64"
11 "gonum.org/v1/gonum/lapack"
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.
17 // Permuting consists of applying a permutation matrix P such that the matrix
18 // that results from P^T*A*P takes the upper block triangular form
20 // P^T A P = [ 0 B Z ],
22 // where T1 and T2 are upper triangular matrices and B contains at least one
23 // nonzero off-diagonal element in each row and column. The indices ilo and ihi
24 // mark the starting and ending columns of the submatrix B. The eigenvalues of A
25 // isolated in the first 0 to ilo-1 and last ihi+1 to n-1 elements on the
26 // diagonal can be read off without any roundoff error.
28 // Scaling consists of applying a diagonal similarity transformation D such that
29 // D^{-1}*B*D has the 1-norm of each row and its corresponding column nearly
30 // equal. The output matrix is
32 // [ 0 inv(D)*B*D inv(D)*Z ].
34 // Scaling may reduce the 1-norm of the matrix, and improve the accuracy of
35 // the computed eigenvalues and/or eigenvectors.
37 // job specifies the operations that will be performed on A.
38 // If job is lapack.None, Dgebal sets scale[i] = 1 for all i and returns ilo=0, ihi=n-1.
39 // If job is lapack.Permute, only permuting will be done.
40 // If job is lapack.Scale, only scaling will be done.
41 // If job is lapack.PermuteScale, both permuting and scaling will be done.
43 // On return, if job is lapack.Permute or lapack.PermuteScale, it will hold that
44 // A[i,j] == 0, for i > j and j ∈ {0, ..., ilo-1, ihi+1, ..., n-1}.
45 // If job is lapack.None or lapack.Scale, or if n == 0, it will hold that
46 // ilo == 0 and ihi == n-1.
48 // On return, scale will contain information about the permutations and scaling
49 // factors applied to A. If π(j) denotes the index of the column interchanged
50 // with column j, and D[j,j] denotes the scaling factor applied to column j,
52 // scale[j] == π(j), for j ∈ {0, ..., ilo-1, ihi+1, ..., n-1},
53 // == D[j,j], for j ∈ {ilo, ..., ihi}.
54 // scale must have length equal to n, otherwise Dgebal will panic.
56 // Dgebal is an internal routine. It is exported for testing purposes.
57 func (impl Implementation) Dgebal(job lapack.Job, n int, a []float64, lda int, scale []float64) (ilo, ihi int) {
61 case lapack.None, lapack.Permute, lapack.Scale, lapack.PermuteScale:
63 checkMatrix(n, n, a, lda)
65 panic("lapack: bad length of scale")
71 if n == 0 || job == lapack.None {
72 for i := range scale {
78 bi := blas64.Implementation()
81 if job == lapack.Scale {
85 // Permutation to isolate eigenvalues if possible.
87 // Search for rows isolating an eigenvalue and push them down.
91 for i := ihi; i >= 0; i-- {
92 for j := 0; j <= ihi; j++ {
100 // Row i has only zero off-diagonal elements in the
101 // block A[ilo:ihi+1,ilo:ihi+1].
102 scale[ihi] = float64(i)
104 bi.Dswap(ihi+1, a[i:], lda, a[ihi:], lda)
105 bi.Dswap(n, a[i*lda:], 1, a[ihi*lda:], 1)
116 // Search for columns isolating an eigenvalue and push them left.
121 for j := ilo; j <= ihi; j++ {
122 for i := ilo; i <= ihi; i++ {
130 // Column j has only zero off-diagonal elements in the
131 // block A[ilo:ihi+1,ilo:ihi+1].
132 scale[ilo] = float64(j)
134 bi.Dswap(ihi+1, a[j:], lda, a[ilo:], lda)
135 bi.Dswap(n-ilo, a[j*lda+ilo:], 1, a[ilo*lda+ilo:], 1)
144 for i := ilo; i <= ihi; i++ {
148 if job == lapack.Permute {
152 // Balance the submatrix in rows ilo to ihi.
155 // sclfac should be a power of 2 to avoid roundoff errors.
156 // Elements of scale are restricted to powers of sclfac,
157 // therefore the matrix will be only nearly balanced.
159 // factor determines the minimum reduction of the row and column
160 // norms that is considered non-negligible. It must be less than 1.
163 sfmin1 := dlamchS / dlamchP
165 sfmin2 := sfmin1 * sclfac
168 // Iterative loop for norm reduction.
172 for i := ilo; i <= ihi; i++ {
173 c := bi.Dnrm2(ihi-ilo+1, a[ilo*lda+i:], lda)
174 r := bi.Dnrm2(ihi-ilo+1, a[i*lda+ilo:], 1)
175 ica := bi.Idamax(ihi+1, a[i:], lda)
176 ca := math.Abs(a[ica*lda+i])
177 ira := bi.Idamax(n-ilo, a[i*lda+ilo:], 1)
178 ra := math.Abs(a[i*lda+ilo+ira])
180 // Guard against zero c or r due to underflow.
181 if c == 0 || r == 0 {
187 for c < g && math.Max(f, math.Max(c, ca)) < sfmax2 && math.Min(r, math.Min(g, ra)) > sfmin2 {
188 if math.IsNaN(c + f + ca + r + g + ra) {
189 // Panic if NaN to avoid infinite loop.
200 for r <= g && math.Max(r, ra) < sfmax2 && math.Min(math.Min(f, c), math.Min(g, ca)) > sfmin2 {
210 // Reduction would be negligible.
213 if f < 1 && scale[i] < 1 && f*scale[i] <= sfmin1 {
216 if f > 1 && scale[i] > 1 && scale[i] >= sfmax1/f {
222 bi.Dscal(n-ilo, 1/f, a[i*lda+ilo:], 1)
223 bi.Dscal(ihi+1, f, a[i:], lda)