diff --git a/Project.toml b/Project.toml index ed3f011..2986670 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ConstructionBase" uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" authors = ["Takafumi Arakaki", "Rafael Schouten", "Jan Weidner"] -version = "1.5.3" +version = "1.5.4" [deps] LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" diff --git a/src/nonstandard.jl b/src/nonstandard.jl index 2ba642d..4b114a7 100644 --- a/src/nonstandard.jl +++ b/src/nonstandard.jl @@ -56,3 +56,21 @@ constructorof(::Type{<:LinRange}) = linrange_constructor ### Expr: args get splatted # ::Expr annotation is to make it type-stable on Julia 1.3- constructorof(::Type{<:Expr}) = (head, args) -> Expr(head, args...)::Expr + +### Cholesky +setproperties(C::LinearAlgebra.Cholesky, patch::NamedTuple{()}) = C +function setproperties(C::LinearAlgebra.Cholesky, patch::NamedTuple{(:L,),<:Tuple{<:LinearAlgebra.LowerTriangular}}) + return LinearAlgebra.Cholesky(C.uplo === 'U' ? copy(patch.L.data') : patch.L.data, C.uplo, C.info) +end +function setproperties(C::LinearAlgebra.Cholesky, patch::NamedTuple{(:U,),<:Tuple{<:LinearAlgebra.UpperTriangular}}) + return LinearAlgebra.Cholesky(C.uplo === 'L' ? copy(patch.U.data') : patch.U.data, C.uplo, C.info) +end +function setproperties( + C::LinearAlgebra.Cholesky, + patch::NamedTuple{(:UL,),<:Tuple{<:Union{LinearAlgebra.LowerTriangular,LinearAlgebra.UpperTriangular}}} +) + return LinearAlgebra.Cholesky(patch.UL.data, C.uplo, C.info) +end +function setproperties(C::LinearAlgebra.Cholesky, patch::NamedTuple) + throw(ArgumentError("Invalid patch for `Cholesky`: $(patch)")) +end diff --git a/test/runtests.jl b/test/runtests.jl index c31ae24..7cee50d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -194,6 +194,52 @@ end @inferred constructorof(typeof(lr1))(getfields(lr2)...) end + @testset "Cholesky" begin + x = randn(3, 3) + X = x * x' + @testset "uplo=$uplo" for uplo in ['L', 'U'] + C = Cholesky(X, uplo, 0) + + # Empty patch. + C_new = ConstructionBase.setproperties(C, NamedTuple()) + @test typeof(C_new) === typeof(C) + for f in propertynames(C) + @test getproperty(C_new, f) == getproperty(C, f) + end + + # Update `L`. + C_new = ConstructionBase.setproperties(C, (L=2 * C.L,)) + @test typeof(C_new) === typeof(C) + for f in propertynames(C) + @test getproperty(C_new, f) == 2 * getproperty(C, f) + end + + # Update `U`. + C_new = ConstructionBase.setproperties(C, (U=2 * C.U,)) + @test typeof(C_new) === typeof(C) + for f in propertynames(C) + @test getproperty(C_new, f) == 2 * getproperty(C, f) + end + + # Update `UL` + C_new = ConstructionBase.setproperties(C, (UL=2 * C.UL,)) + @test typeof(C_new) === typeof(C) + for f in propertynames(C) + @test getproperty(C_new, f) == 2 * getproperty(C, f) + end + + # We can only set the properties with `LowerTriangular` or `UpperTriangular` matrices. + @test_throws ArgumentError ConstructionBase.setproperties(C, (L=parent(C.L),)) + @test_throws ArgumentError ConstructionBase.setproperties(C, (U=parent(C.U),)) + # Can only set one at the time. + @test_throws ArgumentError ConstructionBase.setproperties(C, (L=C.L, U=C.U,)) + @test_throws ArgumentError ConstructionBase.setproperties(C, (UL=C.UL, U=C.U,)) + @test_throws ArgumentError ConstructionBase.setproperties(C, (UL=C.UL, L=C.L,)) + # And make sure any other patch will fail. + @test_throws ArgumentError ConstructionBase.setproperties(C, (asdf=C.UL,)) + end + end + @testset "Expr" begin e = :(a + b) @test e == @inferred constructorof(typeof(e))(getfields(e)...)