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