Skip to content

Commit b4794be

Browse files
committed
Add Cholesky up- and downdates
1 parent 5a0ed4a commit b4794be

File tree

3 files changed

+145
-0
lines changed

3 files changed

+145
-0
lines changed

base/linalg/cholesky.jl

+107
Original file line numberDiff line numberDiff line change
@@ -264,3 +264,110 @@ end
264264
chkfullrank(C::CholeskyPivoted) = C.rank < size(C.factors, 1) && throw(RankDeficientException(C.info))
265265

266266
rank(C::CholeskyPivoted) = C.rank
267+
268+
"""
269+
update!(C::Cholesky, v::StridedVector) -> CC::Cholesky
270+
271+
Update a Cholesky factorization `C` with the vector `v`. If `A = C[:U]'C[:U]` then `CC = cholfact(C[:U]'C[:U] _ v*v')` but the computation of `CC` only uses `O(n^2)` operations. The input factorization `C` is updated in place such that on exit `C == CC`. The vector `v` is destroyed during the computation.
272+
"""
273+
function update!(C::Cholesky, v::StridedVector)
274+
A = C.factors
275+
n = length(v)
276+
if size(C, 1) != n
277+
throw(DimensionMismatch("updating vector must fit size of factorization"))
278+
end
279+
if C.uplo == 'U'
280+
conj!(v)
281+
end
282+
283+
for i = 1:n
284+
285+
# Compute Givens rotation
286+
c, s, r = givensAlgorithm(A[i,i], v[i])
287+
288+
# Store new diagonal element
289+
A[i,i] = r
290+
291+
# Update remaining elements in row/column
292+
if C.uplo == 'U'
293+
for j = i + 1:n
294+
Aij = A[i,j]
295+
vj = v[j]
296+
A[i,j] = c*Aij + s*vj
297+
v[j] = -s'*Aij + c*vj
298+
end
299+
else
300+
for j = i + 1:n
301+
Aji = A[j,i]
302+
vj = v[j]
303+
A[j,i] = c*Aji + s*vj
304+
v[j] = -s'*Aji + c*vj
305+
end
306+
end
307+
end
308+
return C
309+
end
310+
311+
"""
312+
downdate!(C::Cholesky, v::StridedVector) -> CC::Cholesky
313+
314+
Downdate a Cholesky factorization `C` with the vector `v`. If `A = C[:U]'C[:U]` then `CC = cholfact(C[:U]'C[:U] - v*v')` but the computation of `CC` only uses `O(n^2)` operations. The input factorization `C` is updated in place such that on exit `C == CC`. The vector `v` is destroyed during the computation.
315+
"""
316+
function downdate!(C::Cholesky, v::StridedVector)
317+
A = C.factors
318+
n = length(v)
319+
if size(C, 1) != n
320+
throw(DimensionMismatch("updating vector must fit size of factorization"))
321+
end
322+
if C.uplo == 'U'
323+
conj!(v)
324+
end
325+
326+
for i = 1:n
327+
328+
Aii = A[i,i]
329+
330+
# Compute Givens rotation
331+
s = conj(v[i]/Aii)
332+
s2 = abs2(s)
333+
if s2 > 1
334+
throw(LinAlg.PosDefException(i))
335+
end
336+
c = sqrt(1 - abs2(s))
337+
338+
# Store new diagonal element
339+
A[i,i] = c*Aii
340+
341+
# Update remaining elements in row/column
342+
if C.uplo == 'U'
343+
for j = i + 1:n
344+
vj = v[j]
345+
Aij = (A[i,j] - s*vj)/c
346+
A[i,j] = Aij
347+
v[j] = -s'*Aij + c*vj
348+
end
349+
else
350+
for j = i + 1:n
351+
vj = v[j]
352+
Aji = (A[j,i] - s*vj)/c
353+
A[j,i] = Aji
354+
v[j] = -s'*Aji + c*vj
355+
end
356+
end
357+
end
358+
return C
359+
end
360+
361+
"""
362+
update(C::Cholesky, v::StridedVector) -> CC::Cholesky
363+
364+
Update a Cholesky factorization `C` with the vector `v`. If `A = C[:U]'C[:U]` then `CC = cholfact(C[:U]'C[:U] + v*v')` but the computation of `CC` only uses `O(n^2)` operations.
365+
"""
366+
update(C::Cholesky, v::StridedVector) = update!(copy(C), copy(v))
367+
368+
"""
369+
downdate(C::Cholesky, v::StridedVector) -> CC::Cholesky
370+
371+
Downdate a Cholesky factorization `C` with the vector `v`. If `A = C[:U]'C[:U]` then `CC = cholfact(C[:U]'C[:U] - v*v')` but the computation of `CC` only uses `O(n^2)` operations.
372+
"""
373+
downdate(C::Cholesky, v::StridedVector) = downdate!(copy(C), copy(v))

doc/stdlib/linalg.rst

+28
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,34 @@ Linear algebra functions in Julia are largely implemented by calling functions f
145145
146146
``cholfact!`` is the same as :func:`cholfact`, but saves space by overwriting the input ``A``, instead of creating a copy. ``cholfact!`` can also reuse the symbolic factorization from a different matrix ``F`` with the same structure when used as: ``cholfact!(F::CholmodFactor, A)``.
147147

148+
.. currentmodule:: Base.LinAlg
149+
150+
.. function:: update(C::Cholesky, v::StridedVector) -> CC::Cholesky
151+
152+
.. Docstring generated from Julia source
153+
154+
Update a Cholesky factorization ``C`` with the vector ``v``\ . If ``A = C[:U]'C[:U]`` then ``CC = cholfact(C[:U]'C[:U] + v*v')`` but the computation of ``CC`` only uses ``O(n^2)`` operations.
155+
156+
.. function:: downdate(C::Cholesky, v::StridedVector) -> CC::Cholesky
157+
158+
.. Docstring generated from Julia source
159+
160+
Downdate a Cholesky factorization ``C`` with the vector ``v``\ . If ``A = C[:U]'C[:U]`` then ``CC = cholfact(C[:U]'C[:U] - v*v')`` but the computation of ``CC`` only uses ``O(n^2)`` operations.
161+
162+
.. function:: update!(C::Cholesky, v::StridedVector) -> CC::Cholesky
163+
164+
.. Docstring generated from Julia source
165+
166+
Update a Cholesky factorization ``C`` with the vector ``v``\ . If ``A = C[:U]'C[:U]`` then ``CC = cholfact(C[:U]'C[:U] _ v*v')`` but the computation of ``CC`` only uses ``O(n^2)`` operations. The input factorization ``C`` is updated in place such that on exit ``C == CC``\ . The vector ``v`` is destroyed during the computation.
167+
168+
.. function:: downdate!(C::Cholesky, v::StridedVector) -> CC::Cholesky
169+
170+
.. Docstring generated from Julia source
171+
172+
Downdate a Cholesky factorization ``C`` with the vector ``v``\ . If ``A = C[:U]'C[:U]`` then ``CC = cholfact(C[:U]'C[:U] - v*v')`` but the computation of ``CC`` only uses ``O(n^2)`` operations. The input factorization ``C`` is updated in place such that on exit ``C == CC``\ . The vector ``v`` is destroyed during the computation.
173+
174+
.. currentmodule:: Base
175+
148176
.. function:: ldltfact(::SymTridiagonal) -> LDLt
149177

150178
.. Docstring generated from Julia source

test/linalg/cholesky.jl

+10
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,13 @@ for elty in (Float32, Float64, Complex{Float32}, Complex{Float64})
146146
@test_approx_eq full(cholfact(A)[:L]) full(invoke(Base.LinAlg.chol!, Tuple{AbstractMatrix, Type{LowerTriangular}}, copy(A), LowerTriangular))
147147
@test_approx_eq full(cholfact(A)[:U]) full(invoke(Base.LinAlg.chol!, Tuple{AbstractMatrix, Type{UpperTriangular}}, copy(A), UpperTriangular))
148148
end
149+
150+
# Test up- and downdates
151+
let A = complex(randn(10,5), randn(10, 5)), v = complex(randn(5), randn(5))
152+
for uplo in (:U, :L)
153+
AcA = A'A
154+
F = cholfact(AcA, uplo)
155+
@test LinAlg.update(F, v)[uplo] cholfact(AcA + v*v')[uplo]
156+
@test LinAlg.downdate(F, v)[uplo] cholfact(AcA - v*v')[uplo]
157+
end
158+
end

0 commit comments

Comments
 (0)