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  }