diff --git a/README.md b/README.md index 00b6bfa..eab8e92 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,57 @@ # FactoredMatrices +[![CI](https://github.com/HolyLab/FactoredMatrices/actions/workflows/CI.yml/badge.svg)](https://github.com/HolyLab/FactoredMatrices/actions/workflows/CI.yml) +[![codecov](https://codecov.io/gh/HolyLab/FactoredMatrices/branch/master/graph/badge.svg)](https://codecov.io/gh/HolyLab/FactoredMatrices) [![Aqua QA](https://juliatesting.github.io/Aqua.jl/dev/assets/badge.svg)](https://github.com/JuliaTesting/Aqua.jl) -Originally, this package was a sub package of CellSegmentation. +Store and multiply matrices in factored form `M = U*V` without forming the full dense matrix. +When `U` is `m×k` and `V` is `k×n`, a product `A*B` costs `O(mk²n)` instead of `O(m²kn)` — a +significant saving when `k` is small relative to `m` and `n`. -FactoredMatrices provides efficient multiplication methods for plain, adjoint -and transpose of factorized matrices. +## Installation + +```julia +using Pkg +Pkg.add("FactoredMatrices") +``` + +## Usage + +### Basic construction and multiplication + +```julia +using FactoredMatrices + +U = randn(100, 5) # 100×5 +V = randn(5, 80) # 5×80 +A = FactoredMatrix(U, V) # represents the 100×80 matrix U*V + +x = randn(80) +y = A * x # 100-element result, no 100×80 matrix formed + +B = randn(80, 3) +C = A * B # 100×3 result +``` + +`FactoredMatrix` supports adjoints, transposes, `mul!`, `dot`, and conversion to a +dense `Matrix`. + +### Allocation-free repeated multiplication with `Workspace` + +For tight loops where allocations matter, pre-allocate a `Workspace` and pass it +via the `cache` keyword: + +```julia +using LinearAlgebra + +ws = FactoredMatrices.Workspace(A, size(B, 2)) # scratch buffers sized for A*B products + +C = similar(A * B) # pre-allocate output +for _ in 1:1000 + mul!(C, A, B; cache=ws) # no allocation +end +``` + +The same `Workspace` can be reused across calls (including adjoint/transpose products) +as long as the operand sizes do not change. Create one `Workspace` per thread when +multiplying concurrently against the same `A`. diff --git a/src/FactoredMatrices.jl b/src/FactoredMatrices.jl index a80b2e3..7c955bd 100644 --- a/src/FactoredMatrices.jl +++ b/src/FactoredMatrices.jl @@ -1,3 +1,10 @@ +""" + FactoredMatrices + +Store and operate on matrices in factored form `M = U*V` without forming the full +dense matrix. The main type is [`FactoredMatrix`](@ref). For allocation-free repeated +multiplications, create a [`FactoredMatrices.Workspace`](@ref) and pass it to `mul!`. +""" module FactoredMatrices using LinearAlgebra: LinearAlgebra, Adjoint, Factorization, Transpose @@ -8,13 +15,14 @@ export FactoredMatrix VERSION >= v"1.11" && eval(Meta.parse("public Workspace")) """ - FactoredMatrix(U, V) + FactoredMatrix(U::AbstractMatrix{T}, V::AbstractMatrix{T}) where T Store the matrix `M = U*V` in factored form for efficient computation. -Multiplications exploit the factored structure without forming the full dense matrix. +Multiplications exploit the factored structure without forming the full dense +matrix. -For allocation-free repeated multiplications, create a [`Workspace`](@ref) and pass it -as `mul!(C, A, B; cache=ws)`. +For allocation-free repeated multiplications, create a +[`FactoredMatrices.Workspace`](@ref) and pass it as `mul!(C, A, B; cache=ws)`. """ struct FactoredMatrix{T, UT <: AbstractMatrix{T}, VT <: AbstractMatrix{T}} <: Factorization{T} U::UT # m × k