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 }