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