From 2eb53299ec936e30ccd09bff968501315b95d702 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Thu, 29 Oct 2026 14:16:12 +0800 Subject: [PATCH 1/5] R-only QR and LQ decompositions --- .gitignore | 1 + docs/src/man/factorizations.md | 19 +++++++++++ test/factorizations/orth_factors.jl | 50 +++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 test/factorizations/orth_factors.jl diff --git a/.gitignore b/.gitignore index 22b0fc821..3e6da32ea 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ __pycache__ .ipynb* *Manifest.toml +*LocalPreferences.toml .vscode experimental refs diff --git a/docs/src/man/factorizations.md b/docs/src/man/factorizations.md index da0069730..4956ffcec 100644 --- a/docs/src/man/factorizations.md +++ b/docs/src/man/factorizations.md @@ -44,3 +44,22 @@ P ≈ P * P t′ = permute(t, ((1,), (2, 3))); t′ ≈ t′ * P ``` + +To compute only the `R` or `L` factor with QR/LQ-based `left_orth!` and `right_orth!`, pass a zero-dimensional tensor map as the basis output. +Its blocks are empty matrices, so MatrixAlgebraKit skips constructing the orthogonal factor. +The corestriction output must still have the usual intermediate space: + +```julia +V = infimum(fuse(codomain(t)), fuse(domain(t))) +V0 = zerospace(spacetype(t)) +Q0 = similar(t, V0 ← V0) + +R = similar(t, V ← domain(t)) +_, R = left_orth!(copy(t), (Q0, R)) + +L = similar(t, codomain(t) ← V) +L, _ = right_orth!(copy(t), (L, Q0)) +``` + +For a `DiagonalTensorMap`, use an empty `DiagonalTensorMap` as the basis output instead. +Differentiation of these R/L-only paths is not yet supported. diff --git a/test/factorizations/orth_factors.jl b/test/factorizations/orth_factors.jl new file mode 100644 index 000000000..0a734157b --- /dev/null +++ b/test/factorizations/orth_factors.jl @@ -0,0 +1,50 @@ +using TensorKit +using MatrixAlgebraKit: DefaultAlgorithm +using Test + +@testset "Orthogonalization without a basis" begin + for T in (Float32, ComplexF64), (V, W) in ( + (ℂ^7, ℂ^4), + (U1Space(0 => 3, 1 => 2), U1Space(0 => 2, -1 => 1)), + (SU2Space(0 => 3, 1/2 => 1), SU2Space(0 => 1, 1/2 => 2)), + ) + t = randn(T, V ← W) + V0 = zerospace(spacetype(t)) + for A in (t, t'), positive in (false, true), alg in ((; positive), DefaultAlgorithm(; positive)) + Q0 = similar(A, V0 ← V0) + Q, R = left_orth(A; positive) + R0 = similar(R) + Q2, R2 = left_orth!(copy(A), (Q0, R0); alg) + @test Q2 === Q0 + @test R2 === R0 + @test dim(Q2) == 0 + @test space(R2) == space(R) + @test R2 ≈ R + @test Q * R2 ≈ A + + L, Q = right_orth(A; positive) + L0 = similar(L) + L2, Q2 = right_orth!(copy(A), (L0, Q0); alg) + @test Q2 === Q0 + @test L2 === L0 + @test dim(Q2) == 0 + @test space(L2) == space(L) + @test L2 ≈ L + @test L2 * Q ≈ A + end + for positive in (false, true) + d = DiagonalTensorMap(randn(T, reduceddim(V)), V) + Q0 = DiagonalTensorMap(T[], V0) + Q, R = left_orth(d; positive) + Q2, R2 = left_orth!(copy(d), (Q0, similar(R)); positive) + @test Q2 === Q0 + @test R2 ≈ R + @test Q * R2 ≈ d + L, Q = right_orth(d; positive) + L2, Q2 = right_orth!(copy(d), (similar(L), Q0); positive) + @test Q2 === Q0 + @test L2 ≈ L + @test L2 * Q ≈ d + end + end +end From 9a95efbdd7f891e8af4bb1e664c48ef32ca8d30a Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Sun, 1 Nov 2026 08:54:40 +0800 Subject: [PATCH 2/5] Bump MatrixAlgebraKit version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index a759bdaef..bb47d34e3 100644 --- a/Project.toml +++ b/Project.toml @@ -57,7 +57,7 @@ FiniteDifferences = "0.12" GPUArrays = "11.4.1" LRUCache = "1.6" LinearAlgebra = "1" -MatrixAlgebraKit = "0.6.9" +MatrixAlgebraKit = "0.6.14" Mooncake = "0.5.27" OhMyThreads = "0.8.0" PrecompileTools = "1" From bc16a9be791cb113be81c5ba33f39788e1e6ed66 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Sun, 1 Nov 2026 09:04:40 +0800 Subject: [PATCH 3/5] Reorganize tests --- test/factorizations/orth_factors.jl | 50 ----------------------------- test/factorizations/ortho.jl | 30 +++++++++++++++++ 2 files changed, 30 insertions(+), 50 deletions(-) delete mode 100644 test/factorizations/orth_factors.jl diff --git a/test/factorizations/orth_factors.jl b/test/factorizations/orth_factors.jl deleted file mode 100644 index 0a734157b..000000000 --- a/test/factorizations/orth_factors.jl +++ /dev/null @@ -1,50 +0,0 @@ -using TensorKit -using MatrixAlgebraKit: DefaultAlgorithm -using Test - -@testset "Orthogonalization without a basis" begin - for T in (Float32, ComplexF64), (V, W) in ( - (ℂ^7, ℂ^4), - (U1Space(0 => 3, 1 => 2), U1Space(0 => 2, -1 => 1)), - (SU2Space(0 => 3, 1/2 => 1), SU2Space(0 => 1, 1/2 => 2)), - ) - t = randn(T, V ← W) - V0 = zerospace(spacetype(t)) - for A in (t, t'), positive in (false, true), alg in ((; positive), DefaultAlgorithm(; positive)) - Q0 = similar(A, V0 ← V0) - Q, R = left_orth(A; positive) - R0 = similar(R) - Q2, R2 = left_orth!(copy(A), (Q0, R0); alg) - @test Q2 === Q0 - @test R2 === R0 - @test dim(Q2) == 0 - @test space(R2) == space(R) - @test R2 ≈ R - @test Q * R2 ≈ A - - L, Q = right_orth(A; positive) - L0 = similar(L) - L2, Q2 = right_orth!(copy(A), (L0, Q0); alg) - @test Q2 === Q0 - @test L2 === L0 - @test dim(Q2) == 0 - @test space(L2) == space(L) - @test L2 ≈ L - @test L2 * Q ≈ A - end - for positive in (false, true) - d = DiagonalTensorMap(randn(T, reduceddim(V)), V) - Q0 = DiagonalTensorMap(T[], V0) - Q, R = left_orth(d; positive) - Q2, R2 = left_orth!(copy(d), (Q0, similar(R)); positive) - @test Q2 === Q0 - @test R2 ≈ R - @test Q * R2 ≈ d - L, Q = right_orth(d; positive) - L2, Q2 = right_orth!(copy(d), (similar(L), Q0); positive) - @test Q2 === Q0 - @test L2 ≈ L - @test L2 * Q ≈ d - end - end -end diff --git a/test/factorizations/ortho.jl b/test/factorizations/ortho.jl index 170d596a2..d7670a712 100644 --- a/test/factorizations/ortho.jl +++ b/test/factorizations/ortho.jl @@ -52,6 +52,21 @@ for V in spacelist @test Q * R ≈ t @test isisometric(Q) + # Compute only R by passing an empty basis output. + V0 = zerospace(spacetype(t)) + Q0 = t isa DiagonalTensorMap ? DiagonalTensorMap(T[], V0) : similar(t, V0 ← V0) + for positive in (false, true), alg in ((; positive), DefaultAlgorithm(; positive)) + Q, R = @constinferred left_orth(t; positive) + R0 = similar(R) + Q2, R2 = @constinferred left_orth!(copy(t), (Q0, R0); alg) + @test Q2 === Q0 + @test R2 === R0 + @test dim(Q2) == 0 + @test space(R2) == space(R) + @test R2 ≈ R + @test Q * R2 ≈ t + end + N = @constinferred qr_null(t) @test isisometric(N) @test norm(N' * t) ≈ 0 atol = 100 * eps(norm(t)) @@ -146,6 +161,21 @@ for V in spacelist @test L * Q ≈ t @test isisometric(Q; side = :right) + # Compute only L by passing an empty basis output. + V0 = zerospace(spacetype(t)) + Q0 = t isa DiagonalTensorMap ? DiagonalTensorMap(T[], V0) : similar(t, V0 ← V0) + for positive in (false, true), alg in ((; positive), DefaultAlgorithm(; positive)) + L, Q = @constinferred right_orth(t; positive) + L0 = similar(L) + L2, Q2 = @constinferred right_orth!(copy(t), (L0, Q0); alg) + @test Q2 === Q0 + @test L2 === L0 + @test dim(Q2) == 0 + @test space(L2) == space(L) + @test L2 ≈ L + @test L2 * Q ≈ t + end + Nᴴ = @constinferred lq_null(t) @test isisometric(Nᴴ; side = :right) @test norm(t * Nᴴ') ≈ 0 atol = 100 * eps(norm(t)) From 78a6c16651d7bab9d46c228334dd55aca3a32740 Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Sun, 1 Nov 2026 09:11:41 +0800 Subject: [PATCH 4/5] Update docs --- docs/src/man/factorizations.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/src/man/factorizations.md b/docs/src/man/factorizations.md index 4956ffcec..d1fa593df 100644 --- a/docs/src/man/factorizations.md +++ b/docs/src/man/factorizations.md @@ -46,8 +46,6 @@ t′ ≈ t′ * P ``` To compute only the `R` or `L` factor with QR/LQ-based `left_orth!` and `right_orth!`, pass a zero-dimensional tensor map as the basis output. -Its blocks are empty matrices, so MatrixAlgebraKit skips constructing the orthogonal factor. -The corestriction output must still have the usual intermediate space: ```julia V = infimum(fuse(codomain(t)), fuse(domain(t))) From 3cd64f015e98d9dbe462478e48a3e8cc35793e0d Mon Sep 17 00:00:00 2001 From: Yue Zhengyuan Date: Mon, 2 Nov 2026 07:03:07 +0800 Subject: [PATCH 5/5] Drop storage reusing tests --- test/factorizations/ortho.jl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/factorizations/ortho.jl b/test/factorizations/ortho.jl index d7670a712..05f272d80 100644 --- a/test/factorizations/ortho.jl +++ b/test/factorizations/ortho.jl @@ -59,8 +59,6 @@ for V in spacelist Q, R = @constinferred left_orth(t; positive) R0 = similar(R) Q2, R2 = @constinferred left_orth!(copy(t), (Q0, R0); alg) - @test Q2 === Q0 - @test R2 === R0 @test dim(Q2) == 0 @test space(R2) == space(R) @test R2 ≈ R @@ -168,8 +166,6 @@ for V in spacelist L, Q = @constinferred right_orth(t; positive) L0 = similar(L) L2, Q2 = @constinferred right_orth!(copy(t), (L0, Q0); alg) - @test Q2 === Q0 - @test L2 === L0 @test dim(Q2) == 0 @test space(L2) == space(L) @test L2 ≈ L