gonum.org/v1/gonum@v0.14.0/lapack/gonum/dlasq2.go (about)

     1  // Copyright ©2015 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/lapack"
    11  )
    12  
    13  // Dlasq2 computes all the eigenvalues of the symmetric positive
    14  // definite tridiagonal matrix associated with the qd array Z. Eigevalues
    15  // are computed to high relative accuracy avoiding denormalization, underflow
    16  // and overflow.
    17  //
    18  // To see the relation of Z to the tridiagonal matrix, let L be a
    19  // unit lower bidiagonal matrix with sub-diagonals Z(2,4,6,,..) and
    20  // let U be an upper bidiagonal matrix with 1's above and diagonal
    21  // Z(1,3,5,,..). The tridiagonal is L*U or, if you prefer, the
    22  // symmetric tridiagonal to which it is similar.
    23  //
    24  // info returns a status error. The return codes mean as follows:
    25  //
    26  //	0: The algorithm completed successfully.
    27  //	1: A split was marked by a positive value in e.
    28  //	2: Current block of Z not diagonalized after 100*n iterations (in inner
    29  //	   while loop). On exit Z holds a qd array with the same eigenvalues as
    30  //	   the given Z.
    31  //	3: Termination criterion of outer while loop not met (program created more
    32  //	   than N unreduced blocks).
    33  //
    34  // z must have length at least 4*n, and must not contain any negative elements.
    35  // Dlasq2 will panic otherwise.
    36  //
    37  // Dlasq2 is an internal routine. It is exported for testing purposes.
    38  func (impl Implementation) Dlasq2(n int, z []float64) (info int) {
    39  	if n < 0 {
    40  		panic(nLT0)
    41  	}
    42  
    43  	if n == 0 {
    44  		return info
    45  	}
    46  
    47  	if len(z) < 4*n {
    48  		panic(shortZ)
    49  	}
    50  
    51  	if n == 1 {
    52  		if z[0] < 0 {
    53  			panic(negZ)
    54  		}
    55  		return info
    56  	}
    57  
    58  	const cbias = 1.5
    59  
    60  	eps := dlamchP
    61  	safmin := dlamchS
    62  	tol := eps * 100
    63  	tol2 := tol * tol
    64  	if n == 2 {
    65  		if z[1] < 0 || z[2] < 0 {
    66  			panic(negZ)
    67  		} else if z[2] > z[0] {
    68  			z[0], z[2] = z[2], z[0]
    69  		}
    70  		z[4] = z[0] + z[1] + z[2]
    71  		if z[1] > z[2]*tol2 {
    72  			t := 0.5 * (z[0] - z[2] + z[1])
    73  			s := z[2] * (z[1] / t)
    74  			if s <= t {
    75  				s = z[2] * (z[1] / (t * (1 + math.Sqrt(1+s/t))))
    76  			} else {
    77  				s = z[2] * (z[1] / (t + math.Sqrt(t)*math.Sqrt(t+s)))
    78  			}
    79  			t = z[0] + s + z[1]
    80  			z[2] *= z[0] / t
    81  			z[0] = t
    82  		}
    83  		z[1] = z[2]
    84  		z[5] = z[1] + z[0]
    85  		return info
    86  	}
    87  	// Check for negative data and compute sums of q's and e's.
    88  	z[2*n-1] = 0
    89  	emin := z[1]
    90  	var d, e, qmax float64
    91  	var i1, n1 int
    92  	for k := 0; k < 2*(n-1); k += 2 {
    93  		if z[k] < 0 || z[k+1] < 0 {
    94  			panic(negZ)
    95  		}
    96  		d += z[k]
    97  		e += z[k+1]
    98  		qmax = math.Max(qmax, z[k])
    99  		emin = math.Min(emin, z[k+1])
   100  	}
   101  	if z[2*(n-1)] < 0 {
   102  		panic(negZ)
   103  	}
   104  	d += z[2*(n-1)]
   105  	// Check for diagonality.
   106  	if e == 0 {
   107  		for k := 1; k < n; k++ {
   108  			z[k] = z[2*k]
   109  		}
   110  		impl.Dlasrt(lapack.SortDecreasing, n, z)
   111  		z[2*(n-1)] = d
   112  		return info
   113  	}
   114  	trace := d + e
   115  	// Check for zero data.
   116  	if trace == 0 {
   117  		z[2*(n-1)] = 0
   118  		return info
   119  	}
   120  	// Rearrange data for locality: Z=(q1,qq1,e1,ee1,q2,qq2,e2,ee2,...).
   121  	for k := 2 * n; k >= 2; k -= 2 {
   122  		z[2*k-1] = 0
   123  		z[2*k-2] = z[k-1]
   124  		z[2*k-3] = 0
   125  		z[2*k-4] = z[k-2]
   126  	}
   127  	i0 := 0
   128  	n0 := n - 1
   129  
   130  	// Reverse the qd-array, if warranted.
   131  	// z[4*i0-3] --> z[4*(i0+1)-3-1] --> z[4*i0]
   132  	if cbias*z[4*i0] < z[4*n0] {
   133  		ipn4Out := 4 * (i0 + n0 + 2)
   134  		for i4loop := 4 * (i0 + 1); i4loop <= 2*(i0+n0+1); i4loop += 4 {
   135  			i4 := i4loop - 1
   136  			ipn4 := ipn4Out - 1
   137  			z[i4-3], z[ipn4-i4-4] = z[ipn4-i4-4], z[i4-3]
   138  			z[i4-1], z[ipn4-i4-6] = z[ipn4-i4-6], z[i4-1]
   139  		}
   140  	}
   141  
   142  	// Initial split checking via dqd and Li's test.
   143  	pp := 0
   144  	for k := 0; k < 2; k++ {
   145  		d = z[4*n0+pp]
   146  		for i4loop := 4*n0 + pp; i4loop >= 4*(i0+1)+pp; i4loop -= 4 {
   147  			i4 := i4loop - 1
   148  			if z[i4-1] <= tol2*d {
   149  				z[i4-1] = math.Copysign(0, -1)
   150  				d = z[i4-3]
   151  			} else {
   152  				d = z[i4-3] * (d / (d + z[i4-1]))
   153  			}
   154  		}
   155  		// dqd maps Z to ZZ plus Li's test.
   156  		emin = z[4*(i0+1)+pp]
   157  		d = z[4*i0+pp]
   158  		for i4loop := 4*(i0+1) + pp; i4loop <= 4*n0+pp; i4loop += 4 {
   159  			i4 := i4loop - 1
   160  			z[i4-2*pp-2] = d + z[i4-1]
   161  			if z[i4-1] <= tol2*d {
   162  				z[i4-1] = math.Copysign(0, -1)
   163  				z[i4-2*pp-2] = d
   164  				z[i4-2*pp] = 0
   165  				d = z[i4+1]
   166  			} else if safmin*z[i4+1] < z[i4-2*pp-2] && safmin*z[i4-2*pp-2] < z[i4+1] {
   167  				tmp := z[i4+1] / z[i4-2*pp-2]
   168  				z[i4-2*pp] = z[i4-1] * tmp
   169  				d *= tmp
   170  			} else {
   171  				z[i4-2*pp] = z[i4+1] * (z[i4-1] / z[i4-2*pp-2])
   172  				d = z[i4+1] * (d / z[i4-2*pp-2])
   173  			}
   174  			emin = math.Min(emin, z[i4-2*pp])
   175  		}
   176  		z[4*(n0+1)-pp-3] = d
   177  
   178  		// Now find qmax.
   179  		qmax = z[4*(i0+1)-pp-3]
   180  		for i4loop := 4*(i0+1) - pp + 2; i4loop <= 4*(n0+1)+pp-2; i4loop += 4 {
   181  			i4 := i4loop - 1
   182  			qmax = math.Max(qmax, z[i4])
   183  		}
   184  		// Prepare for the next iteration on K.
   185  		pp = 1 - pp
   186  	}
   187  
   188  	// Initialise variables to pass to DLASQ3.
   189  	var ttype int
   190  	var dmin1, dmin2, dn, dn1, dn2, g, tau float64
   191  	var tempq float64
   192  	iter := 2
   193  	var nFail int
   194  	nDiv := 2 * (n0 - i0)
   195  	var i4 int
   196  outer:
   197  	for iwhila := 1; iwhila <= n+1; iwhila++ {
   198  		// Test for completion.
   199  		if n0 < 0 {
   200  			// Move q's to the front.
   201  			for k := 1; k < n; k++ {
   202  				z[k] = z[4*k]
   203  			}
   204  			// Sort and compute sum of eigenvalues.
   205  			impl.Dlasrt(lapack.SortDecreasing, n, z)
   206  			e = 0
   207  			for k := n - 1; k >= 0; k-- {
   208  				e += z[k]
   209  			}
   210  			// Store trace, sum(eigenvalues) and information on performance.
   211  			z[2*n] = trace
   212  			z[2*n+1] = e
   213  			z[2*n+2] = float64(iter)
   214  			z[2*n+3] = float64(nDiv) / float64(n*n)
   215  			z[2*n+4] = 100 * float64(nFail) / float64(iter)
   216  			return info
   217  		}
   218  
   219  		// While array unfinished do
   220  		// e[n0] holds the value of sigma when submatrix in i0:n0
   221  		// splits from the rest of the array, but is negated.
   222  		var desig float64
   223  		var sigma float64
   224  		if n0 != n-1 {
   225  			sigma = -z[4*(n0+1)-2]
   226  		}
   227  		if sigma < 0 {
   228  			info = 1
   229  			return info
   230  		}
   231  		// Find last unreduced submatrix's top index i0, find qmax and
   232  		// emin. Find Gershgorin-type bound if Q's much greater than E's.
   233  		var emax float64
   234  		if n0 > i0 {
   235  			emin = math.Abs(z[4*(n0+1)-6])
   236  		} else {
   237  			emin = 0
   238  		}
   239  		qmin := z[4*(n0+1)-4]
   240  		qmax = qmin
   241  		zSmall := false
   242  		for i4loop := 4 * (n0 + 1); i4loop >= 8; i4loop -= 4 {
   243  			i4 = i4loop - 1
   244  			if z[i4-5] <= 0 {
   245  				zSmall = true
   246  				break
   247  			}
   248  			if qmin >= 4*emax {
   249  				qmin = math.Min(qmin, z[i4-3])
   250  				emax = math.Max(emax, z[i4-5])
   251  			}
   252  			qmax = math.Max(qmax, z[i4-7]+z[i4-5])
   253  			emin = math.Min(emin, z[i4-5])
   254  		}
   255  		if !zSmall {
   256  			i4 = 3
   257  		}
   258  		i0 = (i4+1)/4 - 1
   259  		pp = 0
   260  		if n0-i0 > 1 {
   261  			dee := z[4*i0]
   262  			deemin := dee
   263  			kmin := i0
   264  			for i4loop := 4*(i0+1) + 1; i4loop <= 4*(n0+1)-3; i4loop += 4 {
   265  				i4 := i4loop - 1
   266  				dee = z[i4] * (dee / (dee + z[i4-2]))
   267  				if dee <= deemin {
   268  					deemin = dee
   269  					kmin = (i4+4)/4 - 1
   270  				}
   271  			}
   272  			if (kmin-i0)*2 < n0-kmin && deemin <= 0.5*z[4*n0] {
   273  				ipn4Out := 4 * (i0 + n0 + 2)
   274  				pp = 2
   275  				for i4loop := 4 * (i0 + 1); i4loop <= 2*(i0+n0+1); i4loop += 4 {
   276  					i4 := i4loop - 1
   277  					ipn4 := ipn4Out - 1
   278  					z[i4-3], z[ipn4-i4-4] = z[ipn4-i4-4], z[i4-3]
   279  					z[i4-2], z[ipn4-i4-3] = z[ipn4-i4-3], z[i4-2]
   280  					z[i4-1], z[ipn4-i4-6] = z[ipn4-i4-6], z[i4-1]
   281  					z[i4], z[ipn4-i4-5] = z[ipn4-i4-5], z[i4]
   282  				}
   283  			}
   284  		}
   285  		// Put -(initial shift) into DMIN.
   286  		dmin := -math.Max(0, qmin-2*math.Sqrt(qmin)*math.Sqrt(emax))
   287  
   288  		// Now i0:n0 is unreduced.
   289  		// PP = 0 for ping, PP = 1 for pong.
   290  		// PP = 2 indicates that flipping was applied to the Z array and
   291  		// 		that the tests for deflation upon entry in Dlasq3 should
   292  		// 		not be performed.
   293  		nbig := 100 * (n0 - i0 + 1)
   294  		for iwhilb := 0; iwhilb < nbig; iwhilb++ {
   295  			if i0 > n0 {
   296  				continue outer
   297  			}
   298  
   299  			// While submatrix unfinished take a good dqds step.
   300  			i0, n0, pp, dmin, sigma, desig, qmax, nFail, iter, nDiv, ttype, dmin1, dmin2, dn, dn1, dn2, g, tau =
   301  				impl.Dlasq3(i0, n0, z, pp, dmin, sigma, desig, qmax, nFail, iter, nDiv, ttype, dmin1, dmin2, dn, dn1, dn2, g, tau)
   302  
   303  			pp = 1 - pp
   304  			// When emin is very small check for splits.
   305  			if pp == 0 && n0-i0 >= 3 {
   306  				if z[4*(n0+1)-1] <= tol2*qmax || z[4*(n0+1)-2] <= tol2*sigma {
   307  					splt := i0 - 1
   308  					qmax = z[4*i0]
   309  					emin = z[4*(i0+1)-2]
   310  					oldemn := z[4*(i0+1)-1]
   311  					for i4loop := 4 * (i0 + 1); i4loop <= 4*(n0-2); i4loop += 4 {
   312  						i4 := i4loop - 1
   313  						if z[i4] <= tol2*z[i4-3] || z[i4-1] <= tol2*sigma {
   314  							z[i4-1] = -sigma
   315  							splt = i4 / 4
   316  							qmax = 0
   317  							emin = z[i4+3]
   318  							oldemn = z[i4+4]
   319  						} else {
   320  							qmax = math.Max(qmax, z[i4+1])
   321  							emin = math.Min(emin, z[i4-1])
   322  							oldemn = math.Min(oldemn, z[i4])
   323  						}
   324  					}
   325  					z[4*(n0+1)-2] = emin
   326  					z[4*(n0+1)-1] = oldemn
   327  					i0 = splt + 1
   328  				}
   329  			}
   330  		}
   331  		// Maximum number of iterations exceeded, restore the shift
   332  		// sigma and place the new d's and e's in a qd array.
   333  		// This might need to be done for several blocks.
   334  		info = 2
   335  		i1 = i0
   336  		for {
   337  			tempq = z[4*i0]
   338  			z[4*i0] += sigma
   339  			for k := i0 + 1; k <= n0; k++ {
   340  				tempe := z[4*(k+1)-6]
   341  				z[4*(k+1)-6] *= tempq / z[4*(k+1)-8]
   342  				tempq = z[4*k]
   343  				z[4*k] += sigma + tempe - z[4*(k+1)-6]
   344  			}
   345  			// Prepare to do this on the previous block if there is one.
   346  			if i1 <= 0 {
   347  				break
   348  			}
   349  			n1 = i1 - 1
   350  			for i1 >= 1 && z[4*(i1+1)-6] >= 0 {
   351  				i1 -= 1
   352  			}
   353  			sigma = -z[4*(n1+1)-2]
   354  		}
   355  		for k := 0; k < n; k++ {
   356  			z[2*k] = z[4*k]
   357  			// Only the block 1..N0 is unfinished.  The rest of the e's
   358  			// must be essentially zero, although sometimes other data
   359  			// has been stored in them.
   360  			if k < n0 {
   361  				z[2*(k+1)-1] = z[4*(k+1)-1]
   362  			} else {
   363  				z[2*(k+1)] = 0
   364  			}
   365  		}
   366  		return info
   367  	}
   368  	info = 3
   369  	return info
   370  }