diff --git a/Project.toml b/Project.toml index 403da1e65..a56827cad 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "ChainRulesCore" uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -version = "1.26.5" +version = "1.26.6" [deps] LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" diff --git a/src/projection.jl b/src/projection.jl index ca577aef2..7ddadf11a 100644 --- a/src/projection.jl +++ b/src/projection.jl @@ -452,6 +452,10 @@ end (project::ProjectTo{Diagonal})(dx::Diagonal) = Diagonal(project.diag(dx.diag)) # Symmetric +function _same_uplo(dx::LinearAlgebra.HermOrSym, uplo::Symbol) + return LinearAlgebra.sym_uplo(dx.uplo) === uplo +end +_same_uplo(dx, uplo::Symbol) = true for (SymHerm, chk, fun) in ((:Symmetric, :issymmetric, :transpose), (:Hermitian, :ishermitian, :adjoint)) @eval begin @@ -465,7 +469,11 @@ for (SymHerm, chk, fun) in dy = project.parent(dx) # Here $chk means this is efficient on same-type. # If we could mutate dx, then that could speed up action on dx::Matrix. - dz = $chk(dy) ? dy : (dy .+ $fun(dy)) ./ 2 + # Separate returns avoid a type-unstable `dz`, which allocates on Julia 1.10. + if $chk(dy) && _same_uplo(dy, project.uplo) + return $SymHerm(project.parent(dy), project.uplo) + end + dz = (dy .+ $fun(dy)) ./ 2 return $SymHerm(project.parent(dz), project.uplo) end # This is an example of a subspace which is not a subtype, diff --git a/test/projection.jl b/test/projection.jl index 95046147a..d77788b71 100644 --- a/test/projection.jl +++ b/test/projection.jl @@ -284,6 +284,27 @@ struct NoSuperType end @test pherm(pherm(reshape(1:9, 3, 3))) == pherm(reshape(1:9, 3, 3)) @test pherm(rand(ComplexF32, 3, 3, 1)) isa Hermitian{ComplexF64} + @testset "input with other uplo" begin + for dx in (Symmetric(rand(3, 3), :L), Hermitian(rand(3, 3), :L)) + @test @inferred(psymm(dx)) == dx + @test psymm(dx).uplo == 'U' + end + dx = Hermitian(rand(ComplexF64, 3, 3), :U) + @test @inferred(pherm(dx)) == dx + @test pherm(dx).uplo == 'L' + dx = Symmetric(rand(ComplexF64, 3, 3), :L) + @test @inferred(ProjectTo(Symmetric(rand(ComplexF64, 3, 3)))(dx)) == dx + end + + @testset "plain matrix input that is already symmetric/hermitian" begin + dx = [1.0 2.0 3.0; 2.0 4.0 5.0; 3.0 5.0 6.0] + @test @inferred(psymm(dx)) == dx + @test psymm(dx) isa Symmetric{Float64} + dx = [1.0 2.0+im 3.0; 2.0-im 4.0 5.0im; 3.0 -5.0im 6.0] + @test @inferred(pherm(dx)) == dx + @test pherm(dx) isa Hermitian{ComplexF64} + end + pupp = ProjectTo(UpperTriangular(rand(3, 3))) @test pupp(reshape(1:9, 3, 3)) == [1.0 4.0 7.0; 0.0 5.0 8.0; 0.0 0.0 9.0] @test pupp(pupp(reshape(1:9, 3, 3))) == pupp(reshape(1:9, 3, 3))