github.com/gonum/lapack@v0.0.0-20181123203213-e4cdc5a0bff9/native/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 native
     6  
     7  import (
     8  	"math"
     9  
    10  	"github.com/gonum/blas/blas64"
    11  	"github.com/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^T*A*P takes the upper block triangular form
    19  //            [ T1  X  Y  ]
    20  //  P^T A P = [  0  B  Z  ],
    21  //            [  0  0  T2 ]
    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.
    27  //
    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
    31  //  [ T1     X*D          Y    ]
    32  //  [  0  inv(D)*B*D  inv(D)*Z ].
    33  //  [  0      0           T2   ]
    34  // Scaling may reduce the 1-norm of the matrix, and improve the accuracy of
    35  // the computed eigenvalues and/or eigenvectors.
    36  //
    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.
    42  //
    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.
    47  //
    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,
    51  // then
    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.
    55  //
    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) {
    58  	switch job {
    59  	default:
    60  		panic(badJob)
    61  	case lapack.None, lapack.Permute, lapack.Scale, lapack.PermuteScale:
    62  	}
    63  	checkMatrix(n, n, a, lda)
    64  	if len(scale) != n {
    65  		panic("lapack: bad length of scale")
    66  	}
    67  
    68  	ilo = 0
    69  	ihi = n - 1
    70  
    71  	if n == 0 || job == lapack.None {
    72  		for i := range scale {
    73  			scale[i] = 1
    74  		}
    75  		return ilo, ihi
    76  	}
    77  
    78  	bi := blas64.Implementation()
    79  	swapped := true
    80  
    81  	if job == lapack.Scale {
    82  		goto scaling
    83  	}
    84  
    85  	// Permutation to isolate eigenvalues if possible.
    86  	//
    87  	// Search for rows isolating an eigenvalue and push them down.
    88  	for swapped {
    89  		swapped = false
    90  	rows:
    91  		for i := ihi; i >= 0; i-- {
    92  			for j := 0; j <= ihi; j++ {
    93  				if i == j {
    94  					continue
    95  				}
    96  				if a[i*lda+j] != 0 {
    97  					continue rows
    98  				}
    99  			}
   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)
   103  			if i != ihi {
   104  				bi.Dswap(ihi+1, a[i:], lda, a[ihi:], lda)
   105  				bi.Dswap(n, a[i*lda:], 1, a[ihi*lda:], 1)
   106  			}
   107  			if ihi == 0 {
   108  				scale[0] = 1
   109  				return ilo, ihi
   110  			}
   111  			ihi--
   112  			swapped = true
   113  			break
   114  		}
   115  	}
   116  	// Search for columns isolating an eigenvalue and push them left.
   117  	swapped = true
   118  	for swapped {
   119  		swapped = false
   120  	columns:
   121  		for j := ilo; j <= ihi; j++ {
   122  			for i := ilo; i <= ihi; i++ {
   123  				if i == j {
   124  					continue
   125  				}
   126  				if a[i*lda+j] != 0 {
   127  					continue columns
   128  				}
   129  			}
   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)
   133  			if j != ilo {
   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)
   136  			}
   137  			swapped = true
   138  			ilo++
   139  			break
   140  		}
   141  	}
   142  
   143  scaling:
   144  	for i := ilo; i <= ihi; i++ {
   145  		scale[i] = 1
   146  	}
   147  
   148  	if job == lapack.Permute {
   149  		return ilo, ihi
   150  	}
   151  
   152  	// Balance the submatrix in rows ilo to ihi.
   153  
   154  	const (
   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.
   158  		sclfac = 2
   159  		// factor determines the minimum reduction of the row and column
   160  		// norms that is considered non-negligible. It must be less than 1.
   161  		factor = 0.95
   162  	)
   163  	sfmin1 := dlamchS / dlamchP
   164  	sfmax1 := 1 / sfmin1
   165  	sfmin2 := sfmin1 * sclfac
   166  	sfmax2 := 1 / sfmin2
   167  
   168  	// Iterative loop for norm reduction.
   169  	var conv bool
   170  	for !conv {
   171  		conv = true
   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])
   179  
   180  			// Guard against zero c or r due to underflow.
   181  			if c == 0 || r == 0 {
   182  				continue
   183  			}
   184  			g := r / sclfac
   185  			f := 1.0
   186  			s := c + r
   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.
   190  					panic("lapack: NaN")
   191  				}
   192  				f *= sclfac
   193  				c *= sclfac
   194  				ca *= sclfac
   195  				g /= sclfac
   196  				r /= sclfac
   197  				ra /= sclfac
   198  			}
   199  			g = c / sclfac
   200  			for r <= g && math.Max(r, ra) < sfmax2 && math.Min(math.Min(f, c), math.Min(g, ca)) > sfmin2 {
   201  				f /= sclfac
   202  				c /= sclfac
   203  				ca /= sclfac
   204  				g /= sclfac
   205  				r *= sclfac
   206  				ra *= sclfac
   207  			}
   208  
   209  			if c+r >= factor*s {
   210  				// Reduction would be negligible.
   211  				continue
   212  			}
   213  			if f < 1 && scale[i] < 1 && f*scale[i] <= sfmin1 {
   214  				continue
   215  			}
   216  			if f > 1 && scale[i] > 1 && scale[i] >= sfmax1/f {
   217  				continue
   218  			}
   219  
   220  			// Now balance.
   221  			scale[i] *= f
   222  			bi.Dscal(n-ilo, 1/f, a[i*lda+ilo:], 1)
   223  			bi.Dscal(ihi+1, f, a[i:], lda)
   224  			conv = false
   225  		}
   226  	}
   227  	return ilo, ihi
   228  }