Skip to content

Commit d15793c

Browse files
Fix map_eigvals for fermionic case and test (#250)
Co-authored-by: Matt Fishman <[email protected]>
1 parent f6d2003 commit d15793c

File tree

2 files changed

+119
-10
lines changed

2 files changed

+119
-10
lines changed

src/lib/ITensorsExtensions/src/itensor.jl

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
using LinearAlgebra: LinearAlgebra, eigen, pinv
22
using ITensors:
3+
ITensors,
34
ITensor,
45
Index,
56
commonind,
67
dag,
8+
dir,
79
hasqns,
10+
indices,
811
inds,
912
isdiag,
1013
itensor,
1114
map_diag,
1215
noncommonind,
1316
noprime,
17+
permute,
1418
replaceind,
1519
replaceinds,
1620
sim,
@@ -58,18 +62,57 @@ function eigendecomp(A::ITensor, linds, rinds; ishermitian=false, kwargs...)
5862
D, U = eigen(A, linds, rinds; ishermitian, kwargs...)
5963
ul, ur = noncommonind(D, U), commonind(D, U)
6064
Ul = replaceinds(U, vcat(rinds, ur), vcat(linds, ul))
61-
6265
return Ul, D, dag(U)
6366
end
6467

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

70-
Ul, D, Ur = eigendecomp(A, inds...; ishermitian, kwargs...)
108+
# <fermions>
109+
if fermionic_itensor
110+
# Ensure indices in "matrix" form before re-enabling fermion system
111+
mapped_A = permute(mapped_A, ordered_inds)
112+
ITensors.enable_auto_fermion()
113+
end
71114

72-
return Ul * map_diag(f, D) * Ur
115+
return mapped_A
73116
end
74117

75118
# Analagous to `denseblocks`.

test/test_itensorsextensions.jl

Lines changed: 71 additions & 5 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
@@ -55,9 +60,9 @@ using Test: @test, @testset
5560
rng = StableRNG(1234)
5661
A = random_itensor(rng, elt, i, j)
5762
P = A * prime(dag(A), i)
58-
sqrtP = map_eigvals(sqrt, P, i, i'; ishermitian=true)
59-
inv_P = dag(map_eigvals(inv, P, i, i'; ishermitian=true))
60-
inv_sqrtP = dag(map_eigvals(inv sqrt, P, i, i'; ishermitian=true))
63+
sqrtP = map_eigvals(sqrt, P, [i], [i']; ishermitian=true)
64+
inv_P = dag(map_eigvals(inv, P, [i], [i']; ishermitian=true))
65+
inv_sqrtP = dag(map_eigvals(inv sqrt, P, [i], [i']; ishermitian=true))
6166

6267
new_ind = noprime(sim(i'))
6368
sqrtPdag = replaceind(dag(sqrtP), i', new_ind)
@@ -72,5 +77,66 @@ 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+
ITensors.enable_auto_fermion()
83+
s1 = Index([QN("Nf", 0, -1)=>2, QN("Nf", 1, -1)=>2], "Site,Fermion,n=1")
84+
s2 = Index([QN("Nf", 0, -1)=>2, QN("Nf", 1, -1)=>2], "Site,Fermion,n=2")
85+
86+
# Make a random Hermitian matrix-like 4th order ITensor
87+
T = random_itensor(s1', s2', dag(s2), dag(s1))
88+
T = apply(T, swapprime(dag(T), 0=>1))
89+
@test T swapprime(dag(T), 0=>1) # check Hermitian
90+
91+
Ul, D, Ur = eigendecomp(T, [s1', s2'], [dag(s1), dag(s2)]; ishermitian=true)
92+
93+
@test Ul*D*Ur T
94+
ITensors.disable_auto_fermion()
95+
end
96+
97+
@testset "Fermionic map eigvals tests" begin
98+
ITensors.enable_auto_fermion()
99+
s1 = Index([QN("Nf", 0, -1)=>2, QN("Nf", 1, -1)=>2], "Site,Fermion,n=1")
100+
s2 = Index([QN("Nf", 0, -1)=>2, QN("Nf", 1, -1)=>2], "Site,Fermion,n=2")
101+
102+
# Make a random Hermitian matrix ITensor
103+
M = random_itensor(s1', dag(s1))
104+
#M = mapprime(prime(M)*swapprime(dag(M),0=>1),2=>1)
105+
M = apply(M, swapprime(dag(M), 0=>1))
106+
107+
# Make a random Hermitian matrix-like 4th order ITensor
108+
T = random_itensor(s1', s2', dag(s2), dag(s1))
109+
T = apply(T, swapprime(dag(T), 0=>1))
110+
111+
# Matrix test
112+
sqrtM = map_eigvals(sqrt, M, [s1'], [dag(s1)]; ishermitian=true)
113+
@test M apply(sqrtM, sqrtM)
114+
115+
## Tensor test
116+
sqrtT = map_eigvals(sqrt, T, [s1', s2'], [dag(s1), dag(s2)]; ishermitian=true)
117+
@test T apply(sqrtT, sqrtT)
118+
119+
# Permute and test again
120+
T = permute(T, dag(s2), s2', dag(s1), s1')
121+
sqrtT = map_eigvals(sqrt, T, [s1', s2'], [dag(s1), dag(s2)]; ishermitian=true)
122+
@test T apply(sqrtT, sqrtT)
123+
124+
## Explicitly passing indices in different, valid orders
125+
sqrtT = map_eigvals(sqrt, T, [s2', s1'], [dag(s2), dag(s1)]; ishermitian=true)
126+
@test T apply(sqrtT, sqrtT)
127+
sqrtT = map_eigvals(sqrt, T, [dag(s2), dag(s1)], [s2', s1'], ; ishermitian=true)
128+
@test T apply(sqrtT, sqrtT)
129+
sqrtT = map_eigvals(sqrt, T, [dag(s1), dag(s2)], [s1', s2'], ; ishermitian=true)
130+
@test T apply(sqrtT, sqrtT)
131+
132+
# Test bosonic index case while fermion system is enabled
133+
b = Index([QN("Nb", 0)=>2, QN("Nb", 1)=>2])
134+
T = random_itensor(b', dag(b))
135+
T = apply(T, swapprime(dag(T), 0=>1))
136+
sqrtT = map_eigvals(sqrt, T, [b'], [dag(b)]; ishermitian=true)
137+
@test T apply(sqrtT, sqrtT)
138+
139+
ITensors.disable_auto_fermion()
140+
end
75141
end
76142
end

0 commit comments

Comments
 (0)