Skip to content

Commit 79bdab0

Browse files
committed
Fix map_eigvals for fermionic case and test
1 parent 2d7db49 commit 79bdab0

File tree

2 files changed

+94
-7
lines changed

2 files changed

+94
-7
lines changed

src/lib/ITensorsExtensions/src/itensor.jl

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using LinearAlgebra: LinearAlgebra, eigen, pinv
22
using ITensors:
3+
ITensors,
34
ITensor,
45
Index,
56
commonind,
@@ -58,18 +59,50 @@ function eigendecomp(A::ITensor, linds, rinds; ishermitian=false, kwargs...)
5859
D, U = eigen(A, linds, rinds; ishermitian, kwargs...)
5960
ul, ur = noncommonind(D, U), commonind(D, U)
6061
Ul = replaceinds(U, vcat(rinds, ur), vcat(linds, ul))
61-
6262
return Ul, D, dag(U)
6363
end
6464

65-
function map_eigvals(f::Function, A::ITensor, inds...; ishermitian=false, kwargs...)
65+
function map_eigvals(f::Function, A::ITensor, Linds, Rinds; kws...)
66+
67+
# <fermions>
68+
auto_fermion_enabled = ITensors.using_auto_fermion()
69+
if auto_fermion_enabled
70+
# If fermionic, bring indices into i',j',..,dag(j),dag(i)
71+
# ordering with Out indices coming before In indices
72+
# Resulting tensor acts like a normal matrix (no extra signs
73+
# when taking powers A^n)
74+
if all(j->dir(j)==Out, Linds) && all(j->dir(j)==In, Rinds)
75+
ordered_inds = [Linds..., reverse(Rinds)...]
76+
elseif all(j->dir(j)==Out, Rinds) && all(j->dir(j)==In, Linds)
77+
ordered_inds = [Rinds..., reverse(Linds)...]
78+
else
79+
error(
80+
"For fermionic exp, Linds and Rinds must have same directions within each set. Got dir.(Linds)=",
81+
dir.(Linds),
82+
", dir.(Rinds)=",
83+
dir.(Rinds),
84+
)
85+
end
86+
A = permute(A, ordered_inds)
87+
# A^n now sign free, ok to temporarily disable fermion system
88+
ITensors.disable_auto_fermion()
89+
end
90+
6691
if isdiag(A)
67-
return map_diag(f, A)
92+
mapped_A = map_diag(f, A)
93+
else
94+
Ul, D, Ur = eigendecomp(A, Linds, Rinds; kws...)
95+
mapped_A = Ul * map_diag(f, D) * Ur
6896
end
6997

70-
Ul, D, Ur = eigendecomp(A, inds...; ishermitian, kwargs...)
98+
# <fermions>
99+
if auto_fermion_enabled
100+
# Ensure expA indices in "matrix" form before re-enabling fermion system
101+
mapped_A = permute(mapped_A, ordered_inds)
102+
ITensors.enable_auto_fermion()
103+
end
71104

72-
return Ul * map_diag(f, D) * Ur
105+
return mapped_A
73106
end
74107

75108
# Analagous to `denseblocks`.

test/test_itensorsextensions.jl

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,22 @@ using ITensors:
44
ITensor,
55
Index,
66
QN,
7+
apply,
78
dag,
89
delta,
910
inds,
11+
mapprime,
1012
noprime,
13+
norm,
1114
op,
15+
permute,
1216
prime,
1317
random_itensor,
1418
replaceind,
1519
replaceinds,
16-
sim
17-
using ITensorNetworks.ITensorsExtensions: map_eigvals
20+
sim,
21+
swapprime
22+
using ITensorNetworks.ITensorsExtensions: eigendecomp, map_eigvals
1823
using StableRNGs: StableRNG
1924
using Test: @test, @testset
2025
@testset "ITensorsExtensions" begin
@@ -72,5 +77,54 @@ using Test: @test, @testset
7277
I = replaceind(inv_sqrtP * sqrtP, new_ind, i)
7378
@test I op("I", i)
7479
end
80+
81+
@testset "Fermionic eigendecomp" begin
82+
s1 = Index([QN("Nf", 0, -1)=>2, QN("Nf", 1, -1)=>2], "Site,Fermion,n=1")
83+
s2 = Index([QN("Nf", 0, -1)=>2, QN("Nf", 1, -1)=>2], "Site,Fermion,n=2")
84+
85+
# Make a random Hermitian matrix-like 4th order ITensor
86+
T = random_itensor(s1', s2', dag(s2), dag(s1))
87+
T = apply(T, swapprime(dag(T), 0=>1))
88+
@test T swapprime(dag(T), 0=>1) # check Hermitian
89+
90+
Ul, D, Ur = eigendecomp(T, [s1', s2'], [dag(s1), dag(s2)]; ishermitian=true)
91+
92+
@test Ul*D*Ur T
93+
end
94+
95+
@testset "Fermionic map eigvals tests" begin
96+
s1 = Index([QN("Nf", 0, -1)=>2, QN("Nf", 1, -1)=>2], "Site,Fermion,n=1")
97+
s2 = Index([QN("Nf", 0, -1)=>2, QN("Nf", 1, -1)=>2], "Site,Fermion,n=2")
98+
99+
# Make a random Hermitian matrix ITensor
100+
M = random_itensor(s1', dag(s1))
101+
#M = mapprime(prime(M)*swapprime(dag(M),0=>1),2=>1)
102+
M = apply(M, swapprime(dag(M), 0=>1))
103+
104+
# Make a random Hermitian matrix-like 4th order ITensor
105+
T = random_itensor(s1', s2', dag(s2), dag(s1))
106+
T = apply(T, swapprime(dag(T), 0=>1))
107+
108+
# Matrix test
109+
sqrtM = map_eigvals(sqrt, M, s1', dag(s1); ishermitian=true)
110+
@test M apply(sqrtM, sqrtM)
111+
112+
## Tensor test
113+
sqrtT = map_eigvals(sqrt, T, [s1', s2'], [dag(s1), dag(s2)]; ishermitian=true)
114+
@test T apply(sqrtT, sqrtT)
115+
116+
# Permute and test again
117+
T = permute(T, dag(s2), s2', dag(s1), s1')
118+
sqrtT = map_eigvals(sqrt, T, [s1', s2'], [dag(s1), dag(s2)]; ishermitian=true)
119+
@test T apply(sqrtT, sqrtT)
120+
121+
## Explicitly passing indices in different, valid orders
122+
sqrtT = map_eigvals(sqrt, T, [s2', s1'], [dag(s2), dag(s1)]; ishermitian=true)
123+
@test T apply(sqrtT, sqrtT)
124+
sqrtT = map_eigvals(sqrt, T, [dag(s2), dag(s1)], [s2', s1'], ; ishermitian=true)
125+
@test T apply(sqrtT, sqrtT)
126+
sqrtT = map_eigvals(sqrt, T, [dag(s1), dag(s2)], [s1', s2'], ; ishermitian=true)
127+
@test T apply(sqrtT, sqrtT)
128+
end
75129
end
76130
end

0 commit comments

Comments
 (0)