From 23e2c1bca6d8b60ef1a895d29c5abfe99882964e Mon Sep 17 00:00:00 2001 From: Sheehan Olver Date: Wed, 17 Jun 2026 17:45:49 +0100 Subject: [PATCH 1/7] better support for rdiv with KronExpansionLayout --- src/ContinuumArrays.jl | 5 ++++- src/bases/bases.jl | 8 ++++++++ src/bases/basiskron.jl | 7 ++++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/ContinuumArrays.jl b/src/ContinuumArrays.jl index 6e9a1474..d70ff2d3 100644 --- a/src/ContinuumArrays.jl +++ b/src/ContinuumArrays.jl @@ -13,7 +13,7 @@ import LinearAlgebra: pinv, inv, dot, norm2, ldiv!, mul! import BandedMatrices: AbstractBandedLayout, _BandedMatrix import BlockArrays: block, blockindex, unblock, blockedrange, _BlockedUnitRange, _BlockArray, BlockIndexRange, _maybetail import FillArrays: AbstractFill, getindex_value, SquareEye -import ArrayLayouts: mul, ldiv, ZerosLayout, ScalarLayout, AbstractStridedLayout, check_mul_axes, check_ldiv_axes +import ArrayLayouts: mul, ldiv, ZerosLayout, ScalarLayout, AbstractStridedLayout, check_mul_axes, check_ldiv_axes, Rdiv import QuasiArrays: cardinality, checkindex, QuasiAdjoint, QuasiTranspose, Inclusion, SubQuasiArray, QuasiDiagonal, MulQuasiArray, MulQuasiMatrix, MulQuasiVector, QuasiMatMulMat, QuasiArrayLayout, ApplyQuasiArray, ApplyQuasiMatrix, LazyQuasiArrayApplyStyle, AbstractQuasiArrayApplyStyle, AbstractQuasiLazyLayout, @@ -25,6 +25,9 @@ import QuasiArrays: cardinality, checkindex, QuasiAdjoint, QuasiTranspose, Inclu import InfiniteArrays: Infinity, InfAxes import AbstractFFTs: Plan +const LazyArraysBandedMatricesExt = Base.get_extension(LazyArrays, :LazyArraysBandedMatricesExt) +const BandedLazyLayouts = LazyArraysBandedMatricesExt.BandedLazyLayouts + export Spline, LinearSpline, HeavisideSpline, DiracDelta, Derivative, ℵ₁, Inclusion, Basis, grid, plotgrid, affine, .., transform, expand, plan_transform, basis, coefficients, weaklaplacian, laplacian, Laplacian, AbsLaplacian, abslaplacian diff --git a/src/bases/bases.jl b/src/bases/bases.jl index a18ea8d7..ae25fd3c 100644 --- a/src/bases/bases.jl +++ b/src/bases/bases.jl @@ -118,6 +118,13 @@ end # default to transform for expanding weights copy(L::Ldiv{<:AbstractBasisLayout,<:AbstractWeightLayout}) = transform_ldiv(L.A, L.B) +simplifiable(L::Rdiv{<:AbstractLazyLayout,<:AdjointBasisLayout}) = simplifiable(\, L.B', L.A') +@inline function copy(P::Rdiv{<:AbstractLazyLayout,<:AdjointBasisLayout}) + A, B = P.A, P.B + (B' \ A')' +end + + # multiplication operators, reexpand in basis A @inline function _broadcast_mul_ldiv(::Tuple{Any,AbstractBasisLayout}, A, B) a,b = arguments(B) @@ -455,6 +462,7 @@ LazyArrays._mul_arguments(::ExpansionLayout, A) = LazyArrays._mul_arguments(Appl copy(L::Ldiv{Lay,<:ExpansionLayout}) where Lay<:AbstractBasisLayout = copy(Ldiv{Lay,ApplyLayout{typeof(*)}}(L.A, L.B)) copy(L::Ldiv{Lay,<:ExpansionLayout}) where Lay<:MappedBasisLayouts = copy(Ldiv{Lay,ApplyLayout{typeof(*)}}(L.A, L.B)) copy(L::Mul{<:ExpansionLayout,Lay}) where Lay = copy(Mul{ApplyLayout{typeof(*)},Lay}(L.A, L.B)) +copy(L::Mul{<:ExpansionLayout,Lay}) where Lay<:BandedLazyLayouts = copy(Mul{ApplyLayout{typeof(*)},Lay}(L.A, L.B)) copy(L::Mul{<:ExpansionLayout,Lay}) where Lay<:AbstractLazyLayout = copy(Mul{ApplyLayout{typeof(*)},Lay}(L.A, L.B)) function broadcastbasis_layout(::typeof(+), _, _, a, b) diff --git a/src/bases/basiskron.jl b/src/bases/basiskron.jl index ac7c2966..dbc0fe1e 100644 --- a/src/bases/basiskron.jl +++ b/src/bases/basiskron.jl @@ -22,4 +22,9 @@ function sub_coefficients_layout(::KronExpansionLayout, P, j) X * Bt[:,j] end -sum_layout(::KronExpansionLayout, F, dims...) = sum_layout(ApplyLayout{typeof(*)}(), F, dims...) \ No newline at end of file +sum_layout(::KronExpansionLayout, F, dims...) = sum_layout(ApplyLayout{typeof(*)}(), F, dims...) + +diff_layout(::KronExpansionLayout, F, order...; dims...) = diff_layout(ApplyLayout{typeof(*)}(), F, order...; dims...) + +copy(L::Ldiv{Lay,<:KronExpansionLayout}) where Lay<:AbstractBasisLayout = copy(Ldiv{Lay,ApplyLayout{typeof(*)}}(L.A, L.B)) +copy(L::Rdiv{<:KronExpansionLayout,Lay}) where Lay<:AbstractBasisLayout = copy(Rdiv{ApplyLayout{typeof(*)},Lay}(L.A, L.B)) \ No newline at end of file From 09710ed7501dec74e7165c2d5055edc257bf131d Mon Sep 17 00:00:00 2001 From: Sheehan Olver Date: Wed, 17 Jun 2026 18:16:11 +0100 Subject: [PATCH 2/7] add tests --- test/test_basiskron.jl | 5 +++++ test/test_splines.jl | 9 ++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/test/test_basiskron.jl b/test/test_basiskron.jl index a07ba4a6..f4b335d3 100644 --- a/test/test_basiskron.jl +++ b/test/test_basiskron.jl @@ -22,4 +22,9 @@ end @test F[:,[0.1,0.2]][0.3,:] ≈ F[0.3,[0.1,0.2]] ≈ F[0.3,:][[0.1,0.2]] @test F[[0.1,0.2],:][:,0.3] ≈ F[[0.1,0.2],0.3] ≈ F[:,0.3][[0.1,0.2]] @test F[[0.1,0.2],[0.3,0.4]] ≈ F[[0.1,0.2],:][:,[0.3,0.4]] ≈ F[:,[0.3,0.4]][[0.1,0.2],:] + + @test diff(F)[0.1,0.2] ≈ diff(F;dims=1)[0.1,0.2] ≈ diff(L)[0.1,:]'*C*L[0.2,:] + @test diff(F;dims=2)[0.1,0.2] ≈ L[0.1,:]'*C*diff(L)[0.2,:] + + @test L\F/L' == C end \ No newline at end of file diff --git a/test/test_splines.jl b/test/test_splines.jl index b990d065..81beccbf 100644 --- a/test/test_splines.jl +++ b/test/test_splines.jl @@ -1,6 +1,6 @@ using ContinuumArrays, LinearAlgebra, Base64, FillArrays, QuasiArrays, BandedMatrices, BlockArrays, StatsBase, Random, Test using QuasiArrays: ApplyQuasiArray, ApplyStyle, MemoryLayout, mul, MulQuasiMatrix, Vec -import LazyArrays: MulStyle, LdivStyle, arguments, applied, apply, simplifiable +import LazyArrays: MulStyle, LdivStyle, arguments, applied, apply, simplifiable, ApplyArray import ContinuumArrays: basis, AdjointBasisLayout, ExpansionLayout, BasisLayout, SubBasisLayout, AdjointMappedBasisLayouts, MappedBasisLayout, plan_grid_transform, weaklaplacian Random.seed!(24543) @@ -701,4 +701,11 @@ Random.seed!(24543) g = expand(L[affine(0..1,0..1), :], cos) @test (f + g)[1/9] ≈ exp(1/9) + cos(1/9) end + + @testset "expansion * lazy banded" begin + L = LinearSpline(range(0,1,10)) + F = L * rand(4, 11) + B = brand(11,11,1,1) + @test F * ApplyArray(*, B, B) == F * B*B + end end From 6b8488a4116591eb8b78f01077c1924ef32a5509 Mon Sep 17 00:00:00 2001 From: Sheehan Olver Date: Wed, 17 Jun 2026 18:32:48 +0100 Subject: [PATCH 3/7] Update test_splines.jl --- test/test_splines.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_splines.jl b/test/test_splines.jl index 81beccbf..d8cd5d93 100644 --- a/test/test_splines.jl +++ b/test/test_splines.jl @@ -704,7 +704,7 @@ Random.seed!(24543) @testset "expansion * lazy banded" begin L = LinearSpline(range(0,1,10)) - F = L * rand(4, 11) + F = L * rand(10, 11) B = brand(11,11,1,1) @test F * ApplyArray(*, B, B) == F * B*B end From 35e0a2331748e2521d7b721560259b03a39fb2ec Mon Sep 17 00:00:00 2001 From: Sheehan Olver Date: Wed, 17 Jun 2026 19:07:23 +0100 Subject: [PATCH 4/7] remove unused code and add test --- src/bases/basiskron.jl | 3 +-- test/test_basiskron.jl | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/bases/basiskron.jl b/src/bases/basiskron.jl index dbc0fe1e..7f2ab015 100644 --- a/src/bases/basiskron.jl +++ b/src/bases/basiskron.jl @@ -26,5 +26,4 @@ sum_layout(::KronExpansionLayout, F, dims...) = sum_layout(ApplyLayout{typeof(*) diff_layout(::KronExpansionLayout, F, order...; dims...) = diff_layout(ApplyLayout{typeof(*)}(), F, order...; dims...) -copy(L::Ldiv{Lay,<:KronExpansionLayout}) where Lay<:AbstractBasisLayout = copy(Ldiv{Lay,ApplyLayout{typeof(*)}}(L.A, L.B)) -copy(L::Rdiv{<:KronExpansionLayout,Lay}) where Lay<:AbstractBasisLayout = copy(Rdiv{ApplyLayout{typeof(*)},Lay}(L.A, L.B)) \ No newline at end of file +copy(L::Ldiv{Lay,<:KronExpansionLayout}) where Lay<:AbstractBasisLayout = copy(Ldiv{Lay,ApplyLayout{typeof(*)}}(L.A, L.B)) \ No newline at end of file diff --git a/test/test_basiskron.jl b/test/test_basiskron.jl index f4b335d3..4a67c242 100644 --- a/test/test_basiskron.jl +++ b/test/test_basiskron.jl @@ -26,5 +26,5 @@ end @test diff(F)[0.1,0.2] ≈ diff(F;dims=1)[0.1,0.2] ≈ diff(L)[0.1,:]'*C*L[0.2,:] @test diff(F;dims=2)[0.1,0.2] ≈ L[0.1,:]'*C*diff(L)[0.2,:] - @test L\F/L' == C + @test L\F/L' == (L\F)/L' == L\(F/L') == C end \ No newline at end of file From 99090c343aafdb77137bd56acf37f139b0bea57a Mon Sep 17 00:00:00 2001 From: Sheehan Olver Date: Wed, 17 Jun 2026 20:27:05 +0100 Subject: [PATCH 5/7] add test --- Project.toml | 2 +- test/test_splines.jl | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 42274ca1..e0703bd1 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "ContinuumArrays" uuid = "7ae1f121-cc2c-504b-ac30-9b923412ae5c" -version = "0.20.6" +version = "0.20.7" [deps] AbstractFFTs = "621f4979-c628-5d54-868e-fcf4e3e8185c" diff --git a/test/test_splines.jl b/test/test_splines.jl index d8cd5d93..5f4ea068 100644 --- a/test/test_splines.jl +++ b/test/test_splines.jl @@ -708,4 +708,9 @@ Random.seed!(24543) B = brand(11,11,1,1) @test F * ApplyArray(*, B, B) == F * B*B end + + @testset "adj and rdiv" begin + @test L' / L' ≡ Eye(10) + @test simplifiable(Rdiv(L', L')) ≡ Val(true) + end end From fab82faf6b05c4564fb3c6b295d9c1136f9c79d3 Mon Sep 17 00:00:00 2001 From: Sheehan Olver Date: Thu, 18 Jun 2026 08:01:39 +0100 Subject: [PATCH 6/7] Update test_splines.jl --- test/test_splines.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test_splines.jl b/test/test_splines.jl index 5f4ea068..e45becd5 100644 --- a/test/test_splines.jl +++ b/test/test_splines.jl @@ -1,6 +1,6 @@ using ContinuumArrays, LinearAlgebra, Base64, FillArrays, QuasiArrays, BandedMatrices, BlockArrays, StatsBase, Random, Test using QuasiArrays: ApplyQuasiArray, ApplyStyle, MemoryLayout, mul, MulQuasiMatrix, Vec -import LazyArrays: MulStyle, LdivStyle, arguments, applied, apply, simplifiable, ApplyArray +import LazyArrays: MulStyle, LdivStyle, arguments, applied, apply, simplifiable, ApplyArray, Rdiv import ContinuumArrays: basis, AdjointBasisLayout, ExpansionLayout, BasisLayout, SubBasisLayout, AdjointMappedBasisLayouts, MappedBasisLayout, plan_grid_transform, weaklaplacian Random.seed!(24543) @@ -710,6 +710,7 @@ Random.seed!(24543) end @testset "adj and rdiv" begin + L = LinearSpline(range(0,1,10)) @test L' / L' ≡ Eye(10) @test simplifiable(Rdiv(L', L')) ≡ Val(true) end From 8a2b38e0949a926faadefb95e0236f635a808c6c Mon Sep 17 00:00:00 2001 From: Sheehan Olver Date: Thu, 18 Jun 2026 08:10:01 +0100 Subject: [PATCH 7/7] Update bases.jl --- src/bases/bases.jl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/bases/bases.jl b/src/bases/bases.jl index ae25fd3c..5f683cd5 100644 --- a/src/bases/bases.jl +++ b/src/bases/bases.jl @@ -124,6 +124,11 @@ simplifiable(L::Rdiv{<:AbstractLazyLayout,<:AdjointBasisLayout}) = simplifiable( (B' \ A')' end +@inline function copy(P::Rdiv{ApplyLayout{typeof(*)},<:AdjointBasisLayout}) + A, B = P.A, P.B + (B' \ A')' +end + # multiplication operators, reexpand in basis A @inline function _broadcast_mul_ldiv(::Tuple{Any,AbstractBasisLayout}, A, B)