Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fermionic OpSum to TTN constructor #122

Merged
merged 41 commits into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
cadd5a0
Initial commit, untested addition of compatibility with fermionic sit…
b-kloss Jan 12, 2024
58defab
Remove debugging output.
b-kloss Jan 12, 2024
b53f59d
Add missing end.
b-kloss Jan 12, 2024
b5b37d4
Autofermion sign working for fermions, but not for electrons yet.
b-kloss Jan 12, 2024
614669e
Fix flux for fermions by daggering Op. Still broken when using QNs an…
b-kloss Jan 12, 2024
9cd226b
Add hubbard and tight-binding Hamiltonian to models.
b-kloss Jan 12, 2024
e47fa91
Code working for fermions and electrons. Prior failure due to bug in …
b-kloss Jan 12, 2024
769f51a
Move most of complicated qn/arrow-dir logic to the beginning of ttn_svd.
b-kloss Jan 12, 2024
d1d731f
Cleanup. Leaving debug asserts in for the time being.
b-kloss Jan 12, 2024
82d05f2
Restore state with regards to auto-fermion after test.
b-kloss Jan 12, 2024
f54d59b
Merge branch 'main' into AutoTTNO-fix-fermion
b-kloss Jan 12, 2024
b933301
Restore usual arrow directions.
b-kloss Jan 12, 2024
beeebfc
Fix tests. Lower threshold in tests and workaround an issue when comp…
b-kloss Jan 12, 2024
d17c7ae
Add DMRG test for QNs + fermions on tree.
b-kloss Jan 14, 2024
68eb46d
Fix typo in hubbard model.
b-kloss Jan 14, 2024
a03b4a7
Add ToDo annotation for randomTTN
b-kloss Jan 14, 2024
4135a4f
Format.
b-kloss Jan 14, 2024
c96bb49
Remove commented out models.
b-kloss Jan 15, 2024
c03b359
Implement suggested improvements to model, including general nth-neig…
b-kloss Jan 21, 2024
0d96aff
Adapt test_dmrg and test_dmrg_x, remove usage of Sweeps.
b-kloss Jan 21, 2024
b5738ad
Merge branch 'main' into AutoTTNO-fix-fermion
b-kloss Jan 21, 2024
93ce636
Change use of nsite to nsites.
b-kloss Jan 21, 2024
c1d0909
Refactor fermionic test and add comparison with single-particle energ…
b-kloss Jan 21, 2024
6822fe5
Add comparison with JW string ITensors.MPO for fermions to DMRG.
b-kloss Jan 21, 2024
4b58afd
ITensorGaussianMPS
b-kloss Jan 21, 2024
f6a43e4
Clean up test_opsum_to_ttn.jl
b-kloss Jan 21, 2024
fe8be5c
Format.
b-kloss Jan 21, 2024
53a7b16
Format models.
b-kloss Jan 21, 2024
0eded4f
Apply suggestions from code review
b-kloss Jan 22, 2024
d64c4c7
Minor edits on models and import LinearAlgebra.eigvals in test.
Jan 22, 2024
69138fa
Switch internal index direction to In regardless of whether autofermi…
Jan 22, 2024
ee0fa95
Have non-fermionic QN test in DMRG run with autofermion-sign enabled.
Jan 22, 2024
5993c58
Remove output from src/models.jl
Jan 22, 2024
e0ea0f4
Improve ITensor to matrix conversion.
Jan 22, 2024
4e76b10
Format.
Jan 22, 2024
46b783c
Simplify ITensor Matrix conversion.
Jan 22, 2024
5a24c2a
Simplify expression for index direction and have a shallow copy of si…
Jan 22, 2024
75ba320
Add comment to remove autofermion enabling/disabling part of test onc…
Jan 22, 2024
872d037
Edit comment for copy os sites in ttn_svd.
Jan 22, 2024
0725a9f
Remove outdated comment
mtfishman Jan 22, 2024
24fc702
Style update
mtfishman Jan 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 87 additions & 3 deletions src/models.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,90 @@
_maybe_fill(x, n) = x
_maybe_fill(x::Number, n) = fill(x, n)

#= Make the free fermion Hamiltonian for the up spins
b-kloss marked this conversation as resolved.
Show resolved Hide resolved
os_up = OpSum()
for n in 1:(N - 1)
os_up .+= -t, "Cdagup", n, "Cup", n + 1
os_up .+= -t, "Cdagup", n + 1, "Cup", n
end

# Make the free fermion Hamiltonian for the down spins
os_dn = OpSum()
for n in 1:(N - 1)
os_dn .+= -t, "Cdagdn", n, "Cdn", n + 1
os_dn .+= -t, "Cdagdn", n + 1, "Cdn", n
end
=#
function tight_binding(g::AbstractGraph;t=1.0, tp=0.0, h::Union{<:Real,Vector{<:Real}}=0)

Check warning on line 18 in src/models.jl

View workflow job for this annotation

GitHub Actions / format

[JuliaFormatter] reported by reviewdog 🐶 Raw Output: src/models.jl:18:-function tight_binding(g::AbstractGraph;t=1.0, tp=0.0, h::Union{<:Real,Vector{<:Real}}=0) src/models.jl:18:+function tight_binding(g::AbstractGraph; t=1.0, tp=0.0, h::Union{<:Real,Vector{<:Real}}=0)
h = _maybe_fill(h, nv(g))
ℋ = OpSum()
if !iszero(t)
mtfishman marked this conversation as resolved.
Show resolved Hide resolved
for e in edges(g)
ℋ += -t, "Cdag", maybe_only(src(e)), "C", maybe_only(dst(e))
ℋ += -t, "Cdag", maybe_only(dst(e)), "C", maybe_only(src(e))
end
end
if !iszero(t')
# TODO, more clever way of looping over next to nearest neighbors?
b-kloss marked this conversation as resolved.
Show resolved Hide resolved
mtfishman marked this conversation as resolved.
Show resolved Hide resolved
for (i, v) in enumerate(vertices(g))
nnn = [neighbors(g, n) for n in neighbors(g, v)]
nnn = setdiff(Base.Iterators.flatten(nnn), neighbors(g, v))
nnn = setdiff(nnn, vertices(g)[1:i])
for nn in nnn
ℋ += -tp, "Cdag", maybe_only(v), "C", maybe_only(nn)
b-kloss marked this conversation as resolved.
Show resolved Hide resolved
ℋ += -tp, "Cdag", maybe_only(nn), "C", maybe_only(v)
end
end
end
for (i, v) in enumerate(vertices(g))
if !iszero(h[i])
ℋ -= h[i], "N", maybe_only(v)
end
end
return ℋ
end


Check warning on line 47 in src/models.jl

View workflow job for this annotation

GitHub Actions / format

[JuliaFormatter] reported by reviewdog 🐶 Raw Output: src/models.jl:47:- src/models.jl:48:-

"""
t-t' Hubbard Model
"""
function hubbard(g::AbstractGraph; U=0.0, t=1.0, tp=0.0, h::Union{<:Real,Vector{<:Real}}=0)
h = _maybe_fill(h, nv(g))
ℋ = OpSum()
if !iszero(t)
for e in edges(g)
ℋ += -t, "Cdagup", maybe_only(src(e)), "Cup", maybe_only(dst(e))
ℋ += -t, "Cdagup", maybe_only(dst(e)), "Cup", maybe_only(src(e))
ℋ += -t, "Cdagdn", maybe_only(src(e)), "Cdn", maybe_only(dst(e))
ℋ += -t, "Cdagdn", maybe_only(dst(e)), "Cdn", maybe_only(src(e))
end
end
if !iszero(tp)
# TODO, more clever way of looping over next to nearest neighbors?
for (i, v) in enumerate(vertices(g))
nnn = [neighbors(g, n) for n in neighbors(g, v)]
nnn = setdiff(Base.Iterators.flatten(nnn), neighbors(g, v))
nnn = setdiff(nnn, vertices(g)[1:i])
for nn in nnn
ℋ += -tp, "Cdagup", maybe_only(v), "Cup", maybe_only(nn)
ℋ += -tp, "Cdagup", maybe_only(nn), "Cup", maybe_only(v)
ℋ += -tp, "Cdagdn", maybe_only(v), "Cdn", maybe_only(nn)
ℋ += -tp, "Cdagdn", maybe_only(nn), "Cdn", maybe_only(v)
end
end
end
for (i, v) in enumerate(vertices(g))
if !iszero(h[i])
ℋ -= h[i], "Sz", maybe_only(v)
end
if !iszero(U)
ℋ = U, "Nupdn", maybe_only(v)
end
end
return ℋ
end

"""
Random field J1-J2 Heisenberg model on a general graph
"""
Expand Down Expand Up @@ -43,7 +127,7 @@
ℋ = OpSum()
if !iszero(J1)
for e in edges(g)
ℋ += J1, "Z", maybe_only(src(e)), "Z", maybe_only(dst(e))
ℋ += J1, "Sz", maybe_only(src(e)), "Sz", maybe_only(dst(e))
end
end
if !iszero(J2)
Expand All @@ -53,13 +137,13 @@
nnn = setdiff(Base.Iterators.flatten(nnn), neighbors(g, v))
nnn = setdiff(nnn, vertices(g)[1:i])
for nn in nnn
ℋ += J2, "Z", maybe_only(v), "Z", maybe_only(nn)
ℋ += J2, "Sz", maybe_only(v), "Sz", maybe_only(nn)
end
end
end
for (i, v) in enumerate(vertices(g))
if !iszero(h[i])
ℋ += h[i], "X", maybe_only(v)
ℋ += h[i], "Sx", maybe_only(v)
end
end
return ℋ
Expand Down
90 changes: 40 additions & 50 deletions src/treetensornetworks/opsum_to_ttn.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# Utility methods
#


Check warning on line 7 in src/treetensornetworks/opsum_to_ttn.jl

View workflow job for this annotation

GitHub Actions / format

[JuliaFormatter] reported by reviewdog 🐶 Raw Output: src/treetensornetworks/opsum_to_ttn.jl:7:-
# linear ordering of vertices in tree graph relative to chosen root, chosen outward from root
function find_index_in_tree(site, g::AbstractGraph, root_vertex)
ordering = reverse(post_order_dfs_vertices(g, root_vertex))
Expand Down Expand Up @@ -59,8 +60,10 @@
vertextype_sites = vertextype(sites)
thishasqns = any(v -> hasqns(sites[v]), vertices(sites))

linkdir_ref = ITensors.using_auto_fermion() ? ITensors.In : ITensors.Out
b-kloss marked this conversation as resolved.
Show resolved Hide resolved
# traverse tree outwards from root vertex
vs = reverse(post_order_dfs_vertices(sites, root_vertex)) # store vertices in fixed ordering relative to root
# ToDo: Add check in ttn_svd that the ordering matches that of find_index_in_tree, which is used in sorteachterm #fermion-sign!
es = reverse(reverse.(post_order_dfs_edges(sites, root_vertex))) # store edges in fixed ordering relative to root
# some things to keep track of
degrees = Dict(v => degree(sites, v) for v in vs) # rank of every TTN tensor in network
Expand All @@ -81,7 +84,7 @@
op_cache[ITensors.which_op(st) => ITensors.site(st)] = op_tensor
end
if !isnothing(flux(op_tensor))
q -= flux(op_tensor)
q += flux(op_tensor)
end
end
return q
Expand All @@ -93,13 +96,13 @@
for v in vs
is_internal[v] = isempty(sites[v])
if isempty(sites[v])
push!(sites[v], Index(Hflux => 1)) ###FIXME: using Hflux may be a problem with AutoFermion, the dummy index has to be bosonic (but the QN of H should be too?) ...
push!(sites[v], Index(Hflux => 1))
end
end
inbond_coefs = Dict(
e => Dict{QN,Vector{ITensors.MatElem{coefficient_type}}}() for e in es
) # bond coefficients for incoming edge channels
site_coef_done = Prod{Op}[] # list of terms for which the coefficient has been added to a site factor
site_coef_done = Prod{Op}[] # list of terms for which the coefficient has been added to a site factor
# temporary symbolic representation of TTN Hamiltonian
tempTTN = Dict(v => QNArrElem{Scaled{coefficient_type,Prod{Op}},degrees[v]}[] for v in vs)

Expand Down Expand Up @@ -139,10 +142,10 @@

# compute QNs
incoming_qn = calc_qn(incoming)
not_incoming_qn = calc_qn(not_incoming) ###does this work?
not_incoming_qn = calc_qn(not_incoming)
outgoing_qns = Dict(e => calc_qn(outgoing[e]) for e in edges_out)
site_qn = calc_qn(onsite)

Check warning on line 148 in src/treetensornetworks/opsum_to_ttn.jl

View workflow job for this annotation

GitHub Actions / format

[JuliaFormatter] reported by reviewdog 🐶 Raw Output: src/treetensornetworks/opsum_to_ttn.jl:148:- src/treetensornetworks/opsum_to_ttn.jl:147:+
# initialize QNArrayElement indices and quantum numbers
T_inds = MVector{degrees[v]}(fill(-1, degrees[v]))
T_qns = MVector{degrees[v]}(fill(QN(), degrees[v]))
Expand All @@ -151,9 +154,10 @@
bond_col = -1
if !isempty(incoming)
# get the correct map from edge=>QN to term and channel
coutmap = get!(outmaps, edge_in => -not_incoming_qn, Dict{Vector{Op},Int}())
cinmap = get!(inmaps, edge_in => incoming_qn, Dict{Vector{Op},Int}())
# this checks if term exists on edge=>QN ( otherwise insert it) and returns it's index
coutmap = get!(outmaps, edge_in => not_incoming_qn , Dict{Vector{Op},Int}())

Check warning on line 158 in src/treetensornetworks/opsum_to_ttn.jl

View workflow job for this annotation

GitHub Actions / format

[JuliaFormatter] reported by reviewdog 🐶 Raw Output: src/treetensornetworks/opsum_to_ttn.jl:158:- coutmap = get!(outmaps, edge_in => not_incoming_qn , Dict{Vector{Op},Int}()) src/treetensornetworks/opsum_to_ttn.jl:157:+ coutmap = get!(outmaps, edge_in => not_incoming_qn, Dict{Vector{Op},Int}())
cinmap = get!(inmaps, edge_in => -incoming_qn, Dict{Vector{Op},Int}())

Check warning on line 160 in src/treetensornetworks/opsum_to_ttn.jl

View workflow job for this annotation

GitHub Actions / format

[JuliaFormatter] reported by reviewdog 🐶 Raw Output: src/treetensornetworks/opsum_to_ttn.jl:160:- src/treetensornetworks/opsum_to_ttn.jl:159:+
bond_row = ITensors.posInLink!(cinmap, incoming)
bond_col = ITensors.posInLink!(coutmap, not_incoming) # get incoming channel
bond_coef = convert(coefficient_type, ITensors.coefficient(term))
Expand All @@ -162,14 +166,14 @@
)
push!(q_inbond_coefs, ITensors.MatElem(bond_row, bond_col, bond_coef))
T_inds[dim_in] = bond_col
T_qns[dim_in] = incoming_qn
T_qns[dim_in] = -incoming_qn

Check warning on line 169 in src/treetensornetworks/opsum_to_ttn.jl

View workflow job for this annotation

GitHub Actions / format

[JuliaFormatter] reported by reviewdog 🐶 Raw Output: src/treetensornetworks/opsum_to_ttn.jl:169:- T_qns[dim_in] = -incoming_qn src/treetensornetworks/opsum_to_ttn.jl:168:+ T_qns[dim_in] = -incoming_qn
end
for dout in dims_out
coutmap = get!(
outmaps, edges[dout] => -outgoing_qns[edges[dout]], Dict{Vector{Op},Int}()
outmaps, edges[dout] => outgoing_qns[edges[dout]], Dict{Vector{Op},Int}()
)
T_inds[dout] = ITensors.posInLink!(coutmap, outgoing[edges[dout]]) # add outgoing channel
T_qns[dout] = -outgoing_qns[edges[dout]] # minus sign to account for outgoing arrow direction
T_qns[dout] = outgoing_qns[edges[dout]]
end
# if term starts at this site, add its coefficient as a site factor
site_coef = one(coefficient_type)
Expand All @@ -192,6 +196,7 @@

push!(tempTTN[v], el)
end

ITensors.remove_dups!(tempTTN[v])
# manual truncation: isometry on incoming edge
if !isnothing(dim_in) && !isempty(inbond_coefs[edges[dim_in]])
Expand All @@ -210,20 +215,15 @@
# compress this tempTTN representation into dense form

link_space = Dict{edgetype_sites,Index}()
linkdir = ITensors.using_auto_fermion() ? ITensors.In : ITensors.Out ###FIXME: ??
for e in es
qi = Vector{Pair{QN,Int}}()
push!(qi, QN() => 1)
for (q, Vq) in Vs[e]
cols = size(Vq, 2)
if ITensors.using_auto_fermion() # <fermions>
push!(qi, (-q) => cols)
else
push!(qi, q => cols)
end
push!(qi, q => cols)
end
push!(qi, Hflux => 1)
link_space[e] = Index(qi...; tags=edge_tag(e), dir=linkdir)
link_space[e] = Index(qi...; tags=edge_tag(e), dir=linkdir_ref)
end

H = TTN(sites0) # initialize TTN without the dummy indices added
Expand All @@ -240,12 +240,9 @@
edges = filter(e -> dst(e) == v || src(e) == v, es)
dim_in = findfirst(e -> dst(e) == v, edges)
dims_out = findall(e -> src(e) == v, edges)

# slice isometries at this vertex
Vv = [Vs[e] for e in edges]

linkinds = [link_space[e] for e in edges]
linkdims = dim.(linkinds)

# construct blocks
blocks = Dict{Tuple{Block{degrees[v]},Vector{Op}},Array{coefficient_type,degrees[v]}}()
Expand All @@ -272,32 +269,24 @@

# set non-trivial helper inds
for d in normal_dims
block_helper_inds[d] = qnblock(linkinds[d], T_qns[d])
block_helper_inds[d] = qnblock(linkinds[d], T_qns[d])

Check warning on line 272 in src/treetensornetworks/opsum_to_ttn.jl

View workflow job for this annotation

GitHub Actions / format

[JuliaFormatter] reported by reviewdog 🐶 Raw Output: src/treetensornetworks/opsum_to_ttn.jl:272:- block_helper_inds[d] = qnblock(linkinds[d], T_qns[d]) src/treetensornetworks/opsum_to_ttn.jl:271:+ block_helper_inds[d] = qnblock(linkinds[d], T_qns[d])
end

@assert all(≠(-1), block_helper_inds)# check that all block indices are set
# make Block

Check warning on line 275 in src/treetensornetworks/opsum_to_ttn.jl

View workflow job for this annotation

GitHub Actions / format

[JuliaFormatter] reported by reviewdog 🐶 Raw Output: src/treetensornetworks/opsum_to_ttn.jl:275:- src/treetensornetworks/opsum_to_ttn.jl:274:+
# make and fill Block
theblock = Block(Tuple(block_helper_inds))

# T_qns takes into account incoming/outgoing arrow direction, while Vs are stored for incoming arrow
# flip sign of QN for outgoing arrows, to match incoming arrow definition of Vs
iT_qns = deepcopy(T_qns)
for d in dims_out
iT_qns[d] = -iT_qns[d]
end

if isempty(normal_dims)
M = get!(blocks, (theblock, terms(t)), zero_arr())
@assert isone(length(M))
M[] += ct
else
M = get!(blocks, (theblock, terms(t)), zero_arr())
dim_ranges = Tuple(size(Vv[d][iT_qns[d]], 2) for d in normal_dims)
for c in CartesianIndices(dim_ranges)
dim_ranges = Tuple(size(Vv[d][T_qns[d]], 2) for d in normal_dims)

Check warning on line 284 in src/treetensornetworks/opsum_to_ttn.jl

View workflow job for this annotation

GitHub Actions / format

[JuliaFormatter] reported by reviewdog 🐶 Raw Output: src/treetensornetworks/opsum_to_ttn.jl:284:- dim_ranges = Tuple(size(Vv[d][T_qns[d]], 2) for d in normal_dims) src/treetensornetworks/opsum_to_ttn.jl:283:+ dim_ranges = Tuple(size(Vv[d][T_qns[d]], 2) for d in normal_dims)
for c in CartesianIndices(dim_ranges) # applies isometries in a element-wise manner
z = ct
temp_inds = copy(T_inds)
for (i, d) in enumerate(normal_dims)
V_factor = Vv[d][iT_qns[d]][T_inds[d], c[i]]
V_factor = Vv[d][T_qns[d]][T_inds[d], c[i]]
z *= (d == dim_in ? conj(V_factor) : V_factor) # conjugate incoming isometry factor
temp_inds[d] = c[i]
end
Expand All @@ -308,32 +297,28 @@

H[v] = ITensor()

# make the final arrow directions
_linkinds = copy(linkinds)
if !isnothing(dim_in)
_linkinds[dim_in] = dag(_linkinds[dim_in])
end

for ((b, q_op), m) in blocks
Op = computeSiteProd(sites, Prod(q_op))
if hasqns(Op) ###FIXME: this may not be safe, we may want to check for the equivalent (zero tensor?) case in the dense case as well
if hasqns(Op) # FIXME: this may not be safe, we may want to check for the equivalent (zero tensor?) case in the dense case as well
iszero(nnzblocks(Op)) && continue
end
sq = flux(Op)
if !isnothing(sq)
rq =(b[1]==1 ? Hflux : first(space(linkinds[1])[b[1]])) # get row (dim_in) QN
cq = rq - sq # get column (out_dims) QN
if ITensors.using_auto_fermion()
@assert false
###assuming we want index ordering dim_in, phys..., dims_out...
###but are assembling the tensor by tensor product (dim_in,dims_out...) x (phys...)
###we need to undo the autofermion logic.
###this requires the perm and the quantum numbers
###perm is 1,3,2 (switching phys and dims_out dimensions)
###quantum numbers need to be retrieved from block and linkinds
###basically first(space(linkinds[i])[j]) for (i,j) in block
###this logic requires dim_in to be 1 always, i don't think this is guaranteed
###however, maybe it is required when using autofermion?
###so we would have to figure out a perm to bring it into that format
##compute perm factor to bring into this format, combine with perm factor that results from MPS-like logic
#perm=(1,3,2)
# we need to account for the direct product below ordering the physical indices as the last indices
# although they are in between incoming and outgoing indices in the canonical site-ordering
perm=(1,3,2)
if ITensors.compute_permfactor(perm,rq,sq,cq) == -1
Op .*= -1
end
end
end
T = ITensors.BlockSparseTensor(coefficient_type, [b], _linkinds)
Expand All @@ -342,9 +327,13 @@
if !thishasqns
iT = removeqns(iT)
end

if is_internal[v]
H[v] += iT
else
if hasqns(iT)
@assert flux(iT * Op) == Hflux
end
H[v] += (iT * Op)
end
end
Expand Down Expand Up @@ -411,7 +400,8 @@
return T
end

###CHECK/ToDo: Understand the fermion logic on trees...
# This is almost an exact copy of ITensors/src/opsum_to_mpo_generic:sorteachterm except for the site ordering being
# given via find_index_in_tree
# changed `isless_site` to use tree vertex ordering relative to root
function sorteachterm(os::OpSum, sites::IndsNetwork{V,<:Index}, root_vertex::V) where {V}
os = copy(os)
Expand Down Expand Up @@ -458,7 +448,7 @@
prevsite = currsite

if fermionic
error("No verified fermion support for automatic TTN constructor!") # no verified support, just throw error
#error("No verified fermion support for automatic TTN constructor!") # no verified support, just throw error
b-kloss marked this conversation as resolved.
Show resolved Hide resolved
parity = -parity
else
# Ignore bosonic operators in perm
Expand Down
Loading
Loading