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