Skip to content

Commit c43bd27

Browse files
committed
add projection tests
1 parent d2ed268 commit c43bd27

File tree

3 files changed

+74
-7
lines changed

3 files changed

+74
-7
lines changed

src/TensorKit.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ using LinearAlgebra: norm, dot, normalize, normalize!, tr,
137137
adjoint, adjoint!, transpose, transpose!,
138138
lu, pinv, sylvester,
139139
eigen, eigen!, svd, svd!,
140-
isposdef, isposdef!, ishermitian, rank, cond,
140+
isposdef, isposdef!, rank, cond,
141141
Diagonal, Hermitian
142142
using MatrixAlgebraKit
143143

src/factorizations/matrixalgebrakit.jl

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ for f! in (
4646
end
4747

4848
# Handle these separately because single output instead of tuple
49-
for f! in (:qr_null!, :lq_null!)
49+
for f! in (:qr_null!, :lq_null!, :project_hermitian!, :project_antihermitian!, :project_isometric!)
5050
@eval function MAK.$f!(t::AbstractTensorMap, N, alg::AbstractAlgorithm)
5151
MAK.check_input($f!, t, N, alg)
5252

@@ -444,14 +444,24 @@ end
444444

445445
# Projections
446446
# -----------
447-
function MAK.check_input(::typeof(project_hermitian!), tsrc::AbstractTensorMap, tdst::AbstractTensorMap)
447+
function MAK.check_input(::typeof(project_hermitian!), tsrc::AbstractTensorMap, tdst::AbstractTensorMap, ::AbstractAlgorithm)
448448
domain(tsrc) == codomain(tsrc) || throw(ArgumentError("Hermitian projection requires square input tensor"))
449449
tsrc === tdst || @check_space(tdst, space(tsrc))
450450
return nothing
451451
end
452452

453-
MAK.check_input(::typeof(project_antihermitian!), tsrc::AbstractTensorMap, tdst::AbstractTensorMap) =
454-
MAK.check_input(project_hermitian!, tsrc, tdst)
453+
MAK.check_input(::typeof(project_antihermitian!), tsrc::AbstractTensorMap, tdst::AbstractTensorMap, alg::AbstractAlgorithm) =
454+
MAK.check_input(project_hermitian!, tsrc, tdst, alg)
455455

456-
MAK.initialize_output(::typeof(project_hermitian!), tsrc::AbstractTensorMap) = tsrc
457-
MAK.initialize_output(::typeof(project_antihermitian!), tsrc::AbstractTensorMap) = tsrc
456+
function MAK.check_input(::typeof(project_isometric!), t::AbstractTensorMap, W::AbstractTensorMap, alg::AbstractAlgorithm)
457+
codomain(t) domain(t) || throw(ArgumentError("Isometric projection requires `codomain(t) ≿ domain(t)`"))
458+
@check_space W space(t)
459+
@check_scalar(W, t)
460+
461+
return nothing
462+
end
463+
464+
465+
MAK.initialize_output(::typeof(project_hermitian!), tsrc::AbstractTensorMap, ::AbstractAlgorithm) = tsrc
466+
MAK.initialize_output(::typeof(project_antihermitian!), tsrc::AbstractTensorMap, ::AbstractAlgorithm) = tsrc
467+
MAK.initialize_output(::typeof(project_isometric!), tsrc::AbstractTensorMap, ::AbstractAlgorithm) = similar(tsrc)

test/tensors/factorizations.jl

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,5 +389,62 @@ for V in spacelist
389389
@test cond(t) λmax / λmin
390390
end
391391
end
392+
393+
@testset "Hermitian projections" begin
394+
for T in eltypes,
395+
t in (
396+
rand(T, V1, V1), rand(T, W, W), rand(T, W, W)',
397+
DiagonalTensorMap(rand(T, reduceddim(V1)), V1),
398+
)
399+
normalize!(t)
400+
noisefactor = eps(real(T))^(3 / 4)
401+
402+
th = (t + t') / 2
403+
ta = (t - t') / 2
404+
tc = copy(t)
405+
406+
th′ = @constinferred project_hermitian(t)
407+
@test ishermitian(th′)
408+
@test th′ th
409+
@test t == tc
410+
th_approx = th + noisefactor * ta
411+
@test !ishermitian(th_approx) || (T <: Real && t isa DiagonalTensorMap)
412+
@test ishermitian(th_approx; atol = 10 * noisefactor)
413+
414+
ta′ = project_antihermitian(t)
415+
@test isantihermitian(ta′)
416+
@test ta′ ta
417+
@test t == tc
418+
ta_approx = ta + noisefactor * th
419+
@test !isantihermitian(ta_approx)
420+
@test isantihermitian(ta_approx; atol = 10 * noisefactor) || (T <: Real && t isa DiagonalTensorMap)
421+
end
422+
end
423+
424+
@testset "Isometric projections" begin
425+
for T in eltypes,
426+
t in (
427+
randn(T, W, W), randn(T, W, W)',
428+
randn(T, W, V1), randn(T, V1, W)',
429+
)
430+
t2 = project_isometric(t)
431+
@test isisometric(t2)
432+
t3 = project_isometric(t2)
433+
@test t3 t2 # stability of the projection
434+
@test t2 * (t2' * t) t
435+
436+
tc = similar(t)
437+
t3 = @constinferred project_isometric!(copy!(tc, t), t2)
438+
@test t3 === t2
439+
@test isisometric(t2)
440+
441+
# test that t2 is closer to A then any other isometry
442+
for k in 1:10
443+
δt = randn!(similar(t))
444+
t3 = project_isometric(t + δt / 100)
445+
@test norm(t - t3) > norm(t - t2)
446+
end
447+
end
448+
end
392449
end
393450
end

0 commit comments

Comments
 (0)