1
+ @inline function _to_uplo (char:: Symbol )
2
+ if char == :U
3
+ ' U'
4
+ elseif char == :L
5
+ ' L'
6
+ else
7
+ _throw_uplo ()
8
+ end
9
+ end
10
+ @inline function _to_uplo (char:: Char )
11
+ if char ∈ (' L' , ' U' )
12
+ char
13
+ else
14
+ _throw_uplo ()
15
+ end
16
+ end
17
+ @noinline _throw_uplo () = throw (ArgumentError (" uplo argument must be either :U (upper) or :L (lower)" ))
18
+
19
+ mutable struct BidiagonalConjugationData{T}
20
+ const U:: AbstractMatrix{T} # Typing these concretely prevents the use of Bidiagonal, unless we want LazyBandedMatrices.Bidiagonal
21
+ const C:: AbstractMatrix{T} # Function barriers help to minimise the penalty from this when resizing anyway.
22
+ const dv:: Vector{T}
23
+ const ev:: Vector{T}
24
+ const uplo:: Char
25
+ datasize:: Int # Number of columns
26
+ end
27
+ function BidiagonalConjugationData (U, X, V, uplo:: Char )
28
+ C = X * V
29
+ T = promote_type (typeof (inv (U[1 , 1 ])), eltype (U), eltype (C)) # include inv so that we can't get Ints
30
+ dv, ev = T[], T[]
31
+ return BidiagonalConjugationData (U, C, dv, ev, uplo, 0 )
32
+ end
33
+
34
+ function copy (data:: BidiagonalConjugationData )
35
+ U, C, dv, ev, uplo, datasize = data. U, data. C, data. dv, data. ev, data. uplo, data. datasize
36
+ return BidiagonalConjugationData (copy (U), copy (C), copy (dv), copy (ev), uplo, datasize)
37
+ end
38
+
39
+ function _compute_column_up! (data:: BidiagonalConjugationData , U, C, i)
40
+ dv, ev = data. dv, data. ev
41
+ if i == 1
42
+ dv[i] = C[1 , 1 ] / U[1 , 1 ]
43
+ else
44
+ uᵢ₋₁ᵢ₋₁, uᵢᵢ₋₁, uᵢ₋₁ᵢ, uᵢᵢ = U[i- 1 , i- 1 ], U[i, i- 1 ], U[i- 1 , i], U[i, i]
45
+ cᵢ₋₁ᵢ, cᵢᵢ = C[i- 1 , i], C[i, i]
46
+ Uᵢ⁻¹ = inv (uᵢ₋₁ᵢ₋₁ * uᵢᵢ - uᵢ₋₁ᵢ * uᵢᵢ₋₁)
47
+ dv[i] = Uᵢ⁻¹ * (uᵢ₋₁ᵢ₋₁ * cᵢᵢ - uᵢᵢ₋₁ * cᵢ₋₁ᵢ)
48
+ ev[i- 1 ] = Uᵢ⁻¹ * (uᵢᵢ * cᵢ₋₁ᵢ - uᵢ₋₁ᵢ * cᵢᵢ)
49
+ end
50
+ return data
51
+ end
52
+
53
+ function _compute_column_lo! (data:: BidiagonalConjugationData , U, C, i)
54
+ dv, ev = data. dv, data. ev
55
+ uᵢᵢ, uᵢ₊₁ᵢ, uᵢᵢ₊₁, uᵢ₊₁ᵢ₊₁ = U[i, i], U[i+ 1 , i], U[i, i+ 1 ], U[i+ 1 , i+ 1 ]
56
+ cᵢᵢ, cᵢ₊₁ᵢ = C[i, i], C[i+ 1 , i]
57
+ Uᵢ⁻¹ = inv (uᵢᵢ * uᵢ₊₁ᵢ₊₁ - uᵢᵢ₊₁ * uᵢ₊₁ᵢ)
58
+ dv[i] = Uᵢ⁻¹ * (uᵢ₊₁ᵢ₊₁ * cᵢᵢ - uᵢᵢ₊₁ * cᵢ₊₁ᵢ)
59
+ ev[i] = Uᵢ⁻¹ * (uᵢᵢ * cᵢ₊₁ᵢ - uᵢ₊₁ᵢ * cᵢᵢ)
60
+ return data
61
+ end
62
+
63
+ function _compute_columns! (data:: BidiagonalConjugationData , i)
64
+ U, C = data. U, data. C # Treat _compute_column_(up/lo) as function barriers and take these out early
65
+ return __compute_columns! (data, U, C, i)
66
+ end
67
+ function __compute_columns! (data:: BidiagonalConjugationData , U, C, i)
68
+ ds = data. datasize
69
+ up = data. uplo == ' U'
70
+ for j in (ds+ 1 ): i
71
+ up ? _compute_column_up! (data, U, C, j) : _compute_column_lo! (data, U, C, j)
72
+ end
73
+ data. datasize = i
74
+ return data
75
+ end
76
+
77
+ function resizedata! (data:: BidiagonalConjugationData , n)
78
+ n ≤ 0 && return data
79
+ v = data. datasize
80
+ n = max (v, n)
81
+ dv, ev = data. dv, data. ev
82
+ if n > length (ev) # Avoid O(n²) growing. Note min(length(dv), length(ev)) == length(ev)
83
+ resize! (dv, 2 n + 1 )
84
+ resize! (ev, 2 n)
85
+ end
86
+ n > v && _compute_columns! (data, n)
87
+ return data
88
+ end
89
+
90
+ struct BidiagonalConjugationBand{T} <: LazyVector{T}
91
+ data:: BidiagonalConjugationData{T}
92
+ diag:: Bool # true => diagonal, false => offdiagonal
93
+ end
94
+ @inline size (:: BidiagonalConjugationBand ) = (ℵ₀,)
95
+ @inline resizedata! (A:: BidiagonalConjugationBand , n) = resizedata! (A. data, n)
96
+
97
+ function _bcb_getindex (band:: BidiagonalConjugationBand , I)
98
+ resizedata! (band, maximum (I) + 1 )
99
+ if band. diag
100
+ return band. data. dv[I]
101
+ else
102
+ return band. data. ev[I]
103
+ end
104
+ end
105
+
106
+ @inline getindex (band:: BidiagonalConjugationBand , I:: Integer ) = _bcb_getindex (band, I)
107
+ @inline getindex (band:: BidiagonalConjugationBand , I:: AbstractVector ) = _bcb_getindex (band, I)
108
+
109
+ copy (band:: BidiagonalConjugationBand ) = band
110
+
111
+ const BidiagonalConjugation{T} = Bidiagonal{T,BidiagonalConjugationBand{T}}
112
+
113
+ """
114
+ BidiagonalConjugation(U, X, V, uplo)
115
+
116
+ Efficiently compute the projection of the matrix product
117
+ `inv(U)XV` onto a bidiagonal matrix. The `uplo` argument
118
+ specifies whether the projection is upper (`uplo = 'U'`)
119
+ or lower (`uplo = 'L'`) bidiagonal.
120
+
121
+ The computation is returned as a `Bidiagonal` matrix whose
122
+ diagonal and off-diagonal vectors are computed lazily.
123
+ """
124
+ function BidiagonalConjugation (U, X, V, uplo)
125
+ _uplo = _to_uplo (uplo)
126
+ data = BidiagonalConjugationData (U, X, V, _uplo)
127
+ return _BidiagonalConjugation (data, _uplo)
128
+ end
129
+
130
+ function _BidiagonalConjugation (data, uplo) # need uplo argument so that we can take transposes
131
+ dv = BidiagonalConjugationBand (data, true )
132
+ ev = BidiagonalConjugationBand (data, false )
133
+ return Bidiagonal (dv, ev, uplo)
134
+ end
135
+
136
+ copy (A:: BidiagonalConjugation ) = A # no-op
137
+
138
+ LazyBandedMatrices. Bidiagonal (A:: BidiagonalConjugation ) = LazyBandedMatrices. Bidiagonal (A. dv, A. ev, A. uplo)
0 commit comments