Skip to content

Commit 1f19ca9

Browse files
Exp for fermionic tensors 1188 (#1661)
1 parent 93300cb commit 1f19ca9

File tree

2 files changed

+72
-23
lines changed

2 files changed

+72
-23
lines changed

src/tensor_operations/matrix_algebra.jl

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -43,46 +43,70 @@ function exp(A::ITensor, Linds, Rinds; ishermitian=false)
4343
end
4444
end
4545

46-
N = ndims(A)
4746
NL = length(Linds)
4847
NR = length(Rinds)
4948
NL != NR && error("Must have equal number of left and right indices")
50-
N != NL + NR &&
49+
ndims(A) != NL + NR &&
5150
error("Number of left and right indices must add up to total number of indices")
5251

53-
# Linds, Rinds may not have the correct directions
54-
# TODO: does the need a conversion?
55-
Lis = Linds
56-
Ris = Rinds
52+
# Replace Linds and Rinds with index sets having
53+
# same id's but arrow directions as on A
54+
Linds = permute(commoninds(A, Linds), Linds)
55+
Rinds = permute(commoninds(A, Rinds), Rinds)
5756

58-
# Ensure the indices have the correct directions,
59-
# QNs, etc.
60-
# First grab the indices in A, then permute them
61-
# correctly.
62-
Lis = permute(commoninds(A, Lis), Lis)
63-
Ris = permute(commoninds(A, Ris), Ris)
64-
65-
for (l, r) in zip(Lis, Ris)
57+
# Ensure indices have correct directions, QNs, etc.
58+
for (l, r) in zip(Linds, Rinds)
6659
if space(l) != space(r)
6760
error("In exp, indices must come in pairs with equal spaces.")
6861
end
69-
if hasqns(A)
70-
if dir(l) == dir(r)
71-
error("In exp, indices must come in pairs with opposite directions")
72-
end
62+
if hasqns(A) && dir(l) == dir(r)
63+
error("In exp, indices must come in pairs with opposite directions")
64+
end
65+
end
66+
67+
# <fermions>
68+
auto_fermion_enabled = 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+
)
7385
end
86+
A = permute(A, ordered_inds)
87+
# A^n now sign free, ok to temporarily disable fermion system
88+
disable_auto_fermion()
7489
end
7590

76-
CL = combiner(Lis...; dir=Out)
77-
CR = combiner(Ris...; dir=In)
91+
CL = combiner(Linds...; dir=Out)
92+
CR = combiner(Rinds...; dir=In)
7893
AC = (A * CR) * CL
7994
expAT = ishermitian ? exp(Hermitian(tensor(AC))) : exp(tensor(AC))
80-
return (itensor(expAT) * dag(CR)) * dag(CL)
95+
expA = (itensor(expAT) * dag(CR)) * dag(CL)
96+
97+
# <fermions>
98+
if auto_fermion_enabled
99+
# Ensure expA indices in "matrix" form before re-enabling fermion system
100+
expA = permute(expA, ordered_inds)
101+
enable_auto_fermion()
102+
end
103+
104+
return expA
81105
end
82106

83107
function exp(A::ITensor; kwargs...)
84108
Ris = filterinds(A; plev=0)
85-
Lis = Ris'
109+
Lis = dag.(prime.(Ris))
86110
return exp(A, Lis, Ris; kwargs...)
87111
end
88112

test/base/test_fermions.jl

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using ITensors, Test
22
import ITensors: Out, In
3-
using ITensors.SiteTypes: op, siteinds
3+
using ITensors.SiteTypes: op, siteind, siteinds
44

55
@testset "Fermions" begin
66
ITensors.enable_auto_fermion()
@@ -778,5 +778,30 @@ using ITensors.SiteTypes: op, siteinds
778778
@test norm(a * u - u' * d) 0 atol = (eps(real(eltype(a))))
779779
end
780780

781+
@testset "Fermion exp Tests" begin
782+
s = siteinds("Fermion", 2; conserve_qns=true)
783+
784+
# Matrix test
785+
id_tensor = op("I", s[1])
786+
@test id_tensor exp(0.0 * id_tensor)
787+
788+
# Tensor test
789+
id_tensor = op("I", s[1]) * op("I", s[2])
790+
@test id_tensor exp(0.0 * id_tensor)
791+
792+
# Permute and test again
793+
id_tensor = permute(id_tensor, s[2], s[1], s[2]', s[1]')
794+
@test id_tensor exp(0.0 * id_tensor)
795+
796+
# Explicitly passing indices in different, valid orders
797+
@test id_tensor exp(0.0 * id_tensor, (s[1]', s[2]'), (dag(s[1]), dag(s[2])))
798+
@test id_tensor exp(0.0 * id_tensor, (s[2]', s[1]'), (dag(s[2]), dag(s[1])))
799+
@test id_tensor exp(0.0 * id_tensor, (dag(s[1]), dag(s[2])), (s[1]', s[2]'))
800+
@test id_tensor exp(0.0 * id_tensor, (dag(s[2]), dag(s[1])), (s[2]', s[1]'))
801+
802+
# Check wrong index ordering fails (i.e. we are actually paying attention to it)
803+
@test norm(id_tensor - exp(0.0 * id_tensor, (dag(s[1]), dag(s[2])), (s[2]', s[1]'))) > 1
804+
end
805+
781806
ITensors.disable_auto_fermion()
782807
end

0 commit comments

Comments
 (0)