github.com/gopherd/gonum@v0.0.4/lapack/gonum/dtrexc.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 "github.com/gopherd/gonum/lapack"
     8  
     9  // Dtrexc reorders the real Schur factorization of a n×n real matrix
    10  //  A = Q*T*Qᵀ
    11  // so that the diagonal block of T with row index ifst is moved to row ilst.
    12  //
    13  // On entry, T must be in Schur canonical form, that is, block upper triangular
    14  // with 1×1 and 2×2 diagonal blocks; each 2×2 diagonal block has its diagonal
    15  // elements equal and its off-diagonal elements of opposite sign.
    16  //
    17  // On return, T will be reordered by an orthogonal similarity transformation Z
    18  // as Zᵀ*T*Z, and will be again in Schur canonical form.
    19  //
    20  // If compq is lapack.UpdateSchur, on return the matrix Q of Schur vectors will be
    21  // updated by post-multiplying it with Z.
    22  // If compq is lapack.UpdateSchurNone, the matrix Q is not referenced and will not be
    23  // updated.
    24  // For other values of compq Dtrexc will panic.
    25  //
    26  // ifst and ilst specify the reordering of the diagonal blocks of T. The block
    27  // with row index ifst is moved to row ilst, by a sequence of transpositions
    28  // between adjacent blocks.
    29  //
    30  // If ifst points to the second row of a 2×2 block, ifstOut will point to the
    31  // first row, otherwise it will be equal to ifst.
    32  //
    33  // ilstOut will point to the first row of the block in its final position. If ok
    34  // is true, ilstOut may differ from ilst by +1 or -1.
    35  //
    36  // It must hold that
    37  //  0 <= ifst < n, and  0 <= ilst < n,
    38  // otherwise Dtrexc will panic.
    39  //
    40  // If ok is false, two adjacent blocks were too close to swap because the
    41  // problem is very ill-conditioned. T may have been partially reordered, and
    42  // ilstOut will point to the first row of the block at the position to which it
    43  // has been moved.
    44  //
    45  // work must have length at least n, otherwise Dtrexc will panic.
    46  //
    47  // Dtrexc is an internal routine. It is exported for testing purposes.
    48  func (impl Implementation) Dtrexc(compq lapack.UpdateSchurComp, n int, t []float64, ldt int, q []float64, ldq int, ifst, ilst int, work []float64) (ifstOut, ilstOut int, ok bool) {
    49  	switch {
    50  	case compq != lapack.UpdateSchur && compq != lapack.UpdateSchurNone:
    51  		panic(badUpdateSchurComp)
    52  	case n < 0:
    53  		panic(nLT0)
    54  	case ldt < max(1, n):
    55  		panic(badLdT)
    56  	case ldq < 1, compq == lapack.UpdateSchur && ldq < n:
    57  		panic(badLdQ)
    58  	case (ifst < 0 || n <= ifst) && n > 0:
    59  		panic(badIfst)
    60  	case (ilst < 0 || n <= ilst) && n > 0:
    61  		panic(badIlst)
    62  	}
    63  
    64  	// Quick return if possible.
    65  	if n == 0 {
    66  		return ifst, ilst, true
    67  	}
    68  
    69  	switch {
    70  	case len(t) < (n-1)*ldt+n:
    71  		panic(shortT)
    72  	case compq == lapack.UpdateSchur && len(q) < (n-1)*ldq+n:
    73  		panic(shortQ)
    74  	case len(work) < n:
    75  		panic(shortWork)
    76  	}
    77  
    78  	// Quick return if possible.
    79  	if n == 1 {
    80  		return ifst, ilst, true
    81  	}
    82  
    83  	// Determine the first row of specified block
    84  	// and find out it is 1×1 or 2×2.
    85  	if ifst > 0 && t[ifst*ldt+ifst-1] != 0 {
    86  		ifst--
    87  	}
    88  	nbf := 1 // Size of the first block.
    89  	if ifst+1 < n && t[(ifst+1)*ldt+ifst] != 0 {
    90  		nbf = 2
    91  	}
    92  	// Determine the first row of the final block
    93  	// and find out it is 1×1 or 2×2.
    94  	if ilst > 0 && t[ilst*ldt+ilst-1] != 0 {
    95  		ilst--
    96  	}
    97  	nbl := 1 // Size of the last block.
    98  	if ilst+1 < n && t[(ilst+1)*ldt+ilst] != 0 {
    99  		nbl = 2
   100  	}
   101  
   102  	ok = true
   103  	wantq := compq == lapack.UpdateSchur
   104  
   105  	switch {
   106  	case ifst == ilst:
   107  		return ifst, ilst, true
   108  
   109  	case ifst < ilst:
   110  		// Update ilst.
   111  		switch {
   112  		case nbf == 2 && nbl == 1:
   113  			ilst--
   114  		case nbf == 1 && nbl == 2:
   115  			ilst++
   116  		}
   117  		here := ifst
   118  		for here < ilst {
   119  			// Swap block with next one below.
   120  			if nbf == 1 || nbf == 2 {
   121  				// Current block either 1×1 or 2×2.
   122  				nbnext := 1 // Size of the next block.
   123  				if here+nbf+1 < n && t[(here+nbf+1)*ldt+here+nbf] != 0 {
   124  					nbnext = 2
   125  				}
   126  				ok = impl.Dlaexc(wantq, n, t, ldt, q, ldq, here, nbf, nbnext, work)
   127  				if !ok {
   128  					return ifst, here, false
   129  				}
   130  				here += nbnext
   131  				// Test if 2×2 block breaks into two 1×1 blocks.
   132  				if nbf == 2 && t[(here+1)*ldt+here] == 0 {
   133  					nbf = 3
   134  				}
   135  				continue
   136  			}
   137  
   138  			// Current block consists of two 1×1 blocks each of
   139  			// which must be swapped individually.
   140  			nbnext := 1 // Size of the next block.
   141  			if here+3 < n && t[(here+3)*ldt+here+2] != 0 {
   142  				nbnext = 2
   143  			}
   144  			ok = impl.Dlaexc(wantq, n, t, ldt, q, ldq, here+1, 1, nbnext, work)
   145  			if !ok {
   146  				return ifst, here, false
   147  			}
   148  			if nbnext == 1 {
   149  				// Swap two 1×1 blocks, no problems possible.
   150  				impl.Dlaexc(wantq, n, t, ldt, q, ldq, here, 1, nbnext, work)
   151  				here++
   152  				continue
   153  			}
   154  			// Recompute nbnext in case 2×2 split.
   155  			if t[(here+2)*ldt+here+1] == 0 {
   156  				nbnext = 1
   157  			}
   158  			if nbnext == 2 {
   159  				// 2×2 block did not split.
   160  				ok = impl.Dlaexc(wantq, n, t, ldt, q, ldq, here, 1, nbnext, work)
   161  				if !ok {
   162  					return ifst, here, false
   163  				}
   164  			} else {
   165  				// 2×2 block did split.
   166  				impl.Dlaexc(wantq, n, t, ldt, q, ldq, here, 1, 1, work)
   167  				impl.Dlaexc(wantq, n, t, ldt, q, ldq, here+1, 1, 1, work)
   168  			}
   169  			here += 2
   170  		}
   171  		return ifst, here, true
   172  
   173  	default: // ifst > ilst
   174  		here := ifst
   175  		for here > ilst {
   176  			// Swap block with next one above.
   177  			nbnext := 1
   178  			if here >= 2 && t[(here-1)*ldt+here-2] != 0 {
   179  				nbnext = 2
   180  			}
   181  			if nbf == 1 || nbf == 2 {
   182  				// Current block either 1×1 or 2×2.
   183  				ok = impl.Dlaexc(wantq, n, t, ldt, q, ldq, here-nbnext, nbnext, nbf, work)
   184  				if !ok {
   185  					return ifst, here, false
   186  				}
   187  				here -= nbnext
   188  				// Test if 2×2 block breaks into two 1×1 blocks.
   189  				if nbf == 2 && t[(here+1)*ldt+here] == 0 {
   190  					nbf = 3
   191  				}
   192  				continue
   193  			}
   194  
   195  			// Current block consists of two 1×1 blocks each of
   196  			// which must be swapped individually.
   197  			ok = impl.Dlaexc(wantq, n, t, ldt, q, ldq, here-nbnext, nbnext, 1, work)
   198  			if !ok {
   199  				return ifst, here, false
   200  			}
   201  			if nbnext == 1 {
   202  				// Swap two 1×1 blocks, no problems possible.
   203  				impl.Dlaexc(wantq, n, t, ldt, q, ldq, here, nbnext, 1, work)
   204  				here--
   205  				continue
   206  			}
   207  			// Recompute nbnext in case 2×2 split.
   208  			if t[here*ldt+here-1] == 0 {
   209  				nbnext = 1
   210  			}
   211  			if nbnext == 2 {
   212  				// 2×2 block did not split.
   213  				ok = impl.Dlaexc(wantq, n, t, ldt, q, ldq, here-1, 2, 1, work)
   214  				if !ok {
   215  					return ifst, here, false
   216  				}
   217  			} else {
   218  				// 2×2 block did split.
   219  				impl.Dlaexc(wantq, n, t, ldt, q, ldq, here, 1, 1, work)
   220  				impl.Dlaexc(wantq, n, t, ldt, q, ldq, here-1, 1, 1, work)
   221  			}
   222  			here -= 2
   223  		}
   224  		return ifst, here, true
   225  	}
   226  }