I want to update a matrix inverse using the Sherman-Morrison formula
(A + uv’)^-1 = A^-1 - (A^-1 uv’ A^-1)/(1 + v’A^-1u)
for a large non-sparse matrix inside a loop (a coordinate descent algorithm). Here is a minimum working example for the best code I can find
#mwe rank-1 update
using LinearAlgebra, BenchmarkTools
A = [[1,2] [3,4]]
A_inv = inv(A)
# want to update col 1 to [4,6]
v = [0.0, 1.0]
u = [1,2]
w = Vector{Float64}(undef, 2)
z = Vector{Float64}(undef, 2)
function update_sm!(Λ::AbstractMatrix{<:Real}, u::AbstractVector{<:Real}, v::AbstractVector{<:Real},
w::AbstractVector{<:Real}, z::AbstractVector{<:Real})
# performant way of doing Sherman-Morrison update
# Λ[= Λ - Λ * u*v'Λ ./ (1 .+ v'*Λ * u)
n = size(Λ, 1)
@assert size(Λ, 2) == n
@assert length(u) == n && length(v) == n
@assert length(w) == n && length(z) == n
# Step 2: compute w = Λ * u
mul!(w, Λ, u) # this is faster than the previous version
# Step 1: compute tmp1, but re-using step 2
denom = 1.0 + dot(v, w)
# Step 3: compute z = Λ' * v (i.e., z[j] = sum_i Λ[i,j] * v[i])
mul!(z, Λ', v)
# Step 4: Λ -= (1/denom) * w * z', swap order
scale = 1.0 / denom
@inbounds for j in 1:n
fac = z[j] * scale
for i in 1:n
Λ[i, j] -= fac * w[i]
end
end
return nothing
end
@btime update_lambda_sm_2!($A_inv, $u,$v,$w,$z)
Any way I can speed this up in Julia? The benchmarktools output is currently
55.695 ns (0 allocations: 0 bytes)