Skip to content

Commit 0fbe80c

Browse files
committed
Upgrade tree-DMRG to the ITensorBase 0.8 and TensorAlgebra 0.11 stack
The DMRG code predated the INN v0.7 upgrade. Move it onto the released stack: named arrays and operators now come from ITensorBase rather than NamedDimsArrays (state, operator, apply, lazy, dimnames, replacedimnames, uniquename), the QR and SVD factorizations go through MatrixAlgebraKit (qr_compact, svd_trunc), and the tensor network types are ITensorNetwork and AbstractITensorNetwork. The VectorInterface methods that KrylovKit needs were dropped here and moved into ITensorBase (ITensor/ITensorBase.jl#186), so the Aqua piracy check is back to the full default.
1 parent 7809b5d commit 0fbe80c

9 files changed

Lines changed: 55 additions & 81 deletions

Project.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ SimpleTraits = "699a6c99-e7fa-54fc-8d76-47d257e15c1d"
2424
SplitApplyCombine = "03a91e81-4c3e-53e1-a0a4-9c0c8f19dd66"
2525
TensorAlgebra = "68bd88dc-f39d-4e12-b2ca-f046b68fcc6a"
2626

27+
[sources.ITensorBase]
28+
rev = "mf/vectorinterface-itensor"
29+
url = "https://github.com/ITensor/ITensorBase.jl"
30+
2731
[compat]
2832
Adapt = "4.3"
2933
AlgorithmsInterface = "0.1"

src/ITensorNetworksNext.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ include("beliefpropagation/normnetwork.jl")
2121

2222
include("apply/apply_operators.jl")
2323

24-
include("nameddimsarrays_extensions.jl")
2524
include("tensornetworkoperator.jl")
2625
include("quadraticformnetwork.jl")
2726
include("orthogonalize.jl")

src/dmrg.jl

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import .AlgorithmsInterfaceExtensions as AIE
22
import AlgorithmsInterface as AI
33
using Base: @kwdef
44
using Graphs: dst, edges, edgetype, src
5+
using ITensorBase:
6+
ITensorBase as ITB, dimnames, operator, replacedimnames, state, uniquename
57
using KrylovKit: eigsolve
6-
using NamedDimsArrays: NamedDimsArrays as NDA, dimnames, replacedimnames
8+
using MatrixAlgebraKit: svd_trunc
79
using NamedGraphs.GraphsExtensions: edge_path
8-
using TensorAlgebra: TensorAlgebra as TA
910

1011
# ============================ Top-level entry point =====================================
1112

@@ -35,7 +36,7 @@ function dmrg(
3536
trunc = nothing,
3637
regions = default_dmrg_regions(ket),
3738
link_index_map = Dict(
38-
ln => randname(ln) for e in edges(ket) for ln in linknames(ket, e)
39+
ln => uniquename(ln) for e in edges(ket) for ln in linknames(ket, e)
3940
),
4041
alg = nothing,
4142
kwargs...
@@ -361,44 +362,42 @@ function region_update_nsite!(::Val{2}, alg::TwoSiteEigsolve, qf, env, region)
361362
return qf, energy
362363
end
363364

364-
function svd_split(T, rows; trunc)
365-
return isnothing(trunc) ? TA.svd(T, rows) : TA.svd(T, rows; trunc)
366-
end
365+
svd_split(T, rows; trunc) = svd_trunc(T, rows; trunc)
367366

368367
# Lowest eigenpair of the effective Hamiltonian operator `H_eff` acting on the named tensor
369-
# `T` (via `NamedDimsArrays.apply`), using `KrylovKit.eigsolve`.
368+
# `T` (via `ITensorBase.apply`), using `KrylovKit.eigsolve`.
370369
function eigsolve_named(H_eff, T, which)
371-
vals, vecs = eigsolve(x -> NDA.apply(H_eff, x), T, 1, which; ishermitian = true)
370+
vals, vecs = eigsolve(x -> ITB.apply(H_eff, x), T, 1, which; ishermitian = true)
372371
return real(vals[1]), vecs[1]
373372
end
374373

375374
# The effective Hamiltonian for a `region` (a vector of vertices) is the projected operator
376375
# obtained by contracting the operator tensors on the region with the incoming environment
377376
# messages on the region's boundary. Contracting those gives a single tensor whose codomain
378377
# (output) names are the region's bra names and whose domain (input) names are its ket names
379-
# — i.e. exactly a `NamedDimsArrays` operator. So `effective_hamiltonian` returns that
380-
# operator, and its action on a region ket tensor `T` is `NamedDimsArrays.apply(H, T)` (which
381-
# contracts the ket names and renames the resulting bra names back to ket names).
378+
# — i.e. exactly an ITensor operator. So `effective_hamiltonian` returns that operator, and
379+
# its action on a region ket tensor `T` is `ITensorBase.apply(H, T)` (which contracts the ket
380+
# names and renames the resulting bra names back to ket names).
382381

383382
"""
384-
effective_hamiltonian(qf::QuadraticFormNetwork, env, region) -> NamedDimsOperator
383+
effective_hamiltonian(qf::QuadraticFormNetwork, env, region) -> ITensorOperator
385384
386-
Effective (projected) Hamiltonian for `region` (a vector of vertices) as a `NamedDimsArrays`
385+
Effective (projected) Hamiltonian for `region` (a vector of vertices) as an ITensor
387386
operator: its domain (input) names are the region's ket names and its codomain (output)
388387
names are the matching bra names. Apply it to a region ket tensor `T` with
389-
`NamedDimsArrays.apply`. The environment `env` is a `MessageCache` of
388+
`ITensorBase.apply`. The environment `env` is a `MessageCache` of
390389
[`quadratic_form_environments`](@ref).
391390
"""
392391
function effective_hamiltonian(qf::QuadraticFormNetwork, env, region)
393392
operators = [operator_tensor(qf, v) for v in region]
394-
boundary = [NDA.state(m) for m in incoming_edge_data(env, region)]
393+
boundary = [state(m) for m in incoming_edge_data(env, region)]
395394
h = contract_network([operators; boundary])
396395
sitemap = site_index_map(qf.operator)
397396
ketnames = [
398397
n for n in dimnames(h) if haskey(sitemap, n) || haskey(qf.link_index_map, n)
399398
]
400399
branames = [bra_name_map(qf)[n] for n in ketnames]
401-
return NDA.operator(h, branames, ketnames)
400+
return operator(h, branames, ketnames)
402401
end
403402

404403
# ============================ Energy-based convergence ==================================

src/nameddimsarrays_extensions.jl

Lines changed: 0 additions & 22 deletions
This file was deleted.

src/orthogonalize.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using Graphs: dst, src
2-
using NamedDimsArrays: dimnames, replacedimnames
2+
using ITensorBase: dimnames, replacedimnames
3+
using MatrixAlgebraKit: qr_compact
34
using NamedGraphs.GraphsExtensions: edge_path, post_order_dfs_edges
4-
using TensorAlgebra: TensorAlgebra as TA
55

66
# Isometric (QR) gauge on a tree. The orthogonality center is *not* stored on the network
77
# — it is tracked by the caller. `orthogonalize(state, center)` canonicalizes the whole
@@ -19,7 +19,7 @@ function gauge_move!(state, v, w)
1919
ln = only(linknames(state, v => w))
2020
tv = state[v]
2121
rows = collect(setdiff(dimnames(tv), [ln]))
22-
Q, R = TA.qr(tv, rows)
22+
Q, R = qr_compact(tv, rows)
2323
r = only(setdiff(dimnames(Q), rows))
2424
new_w = R * state[w]
2525
setindex_preserve_graph!(state, replacedimnames(Q, r => ln), v)

src/quadraticformnetwork.jl

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1-
using .LazyNamedDimsArrays: lazy
21
using DataGraphs: DataGraphs, underlying_graph
32
using Graphs: dst, edgetype, neighbors, src, vertices
4-
using NamedDimsArrays: NamedDimsArrays as NDA, dimnames, replacedimnames
3+
using ITensorBase: dimnames, lazy, operator, replacedimnames, state
54
using NamedGraphs.GraphsExtensions: vertextype
65

7-
# A lazy `⟨ψ|H|ψ⟩` network: a ket `TensorNetwork`, a `TensorNetworkOperator` (which carries
6+
# A lazy `⟨ψ|H|ψ⟩` network: a ket `ITensorNetwork`, a `TensorNetworkOperator` (which carries
87
# the ket → bra *site* name map), and a forward ket → bra map for the *link* names. The bra
98
# layer is derived from the ket (`conj` + index renaming), never stored, so updating a ket
10-
# tensor is reflected in the bra. As an `AbstractTensorNetwork`, the data on vertex `v` is
9+
# tensor is reflected in the bra. As an `AbstractITensorNetwork`, the data on vertex `v` is
1110
# the lazy product `lazy(ket) * lazy(operator) * lazy(bra)`, so the existing
1211
# `contract_network` / `MessageCache` machinery treats it like any other tensor network.
1312
struct QuadraticFormNetwork{V, VD, Ket, Operator, LinkMap} <:
14-
AbstractTensorNetwork{V, VD}
13+
AbstractITensorNetwork{V, VD}
1514
ket::Ket
1615
operator::Operator
1716
link_index_map::LinkMap
@@ -79,22 +78,22 @@ end
7978

8079
function incoming_subtree_messages(messages, graph, v, w)
8180
return [
82-
NDA.state(messages[edgetype(graph)(u, v)]) for
81+
state(messages[edgetype(graph)(u, v)]) for
8382
u in neighbors(graph, v) if u != w
8483
]
8584
end
8685

8786
function environment_operator(message, link_index_map)
8887
ketnames = [n for n in dimnames(message) if haskey(link_index_map, n)]
8988
branames = [link_index_map[n] for n in ketnames]
90-
return NDA.operator(message, branames, ketnames)
89+
return operator(message, branames, ketnames)
9190
end
9291

9392
"""
9493
quadratic_form_environments(qf::QuadraticFormNetwork; root) -> MessageCache
9594
9695
Exact projected-Hamiltonian environments of `⟨ψ|H|ψ⟩` on a tree, as a `MessageCache` of
97-
`NamedDimsArrays` operators keyed by directed edges. The message on `v → w` is the
96+
ITensor operators keyed by directed edges. The message on `v → w` is the
9897
contraction of the `⟨ψ|H|ψ⟩` subtree on `v`'s side of `(v, w)`, wrapped as an operator
9998
recording the bra ↔ ket link correspondence (see [`environment_operator`](@ref)).
10099
"""

src/tensornetworkoperator.jl

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,32 @@
11
using DataGraphs: DataGraphs, underlying_graph
2-
using NamedDimsArrays: NamedDimsArrays as NDA
2+
using ITensorBase: ITensorBase as ITB
33
using NamedGraphs.GraphsExtensions: vertextype
44

5-
# A tensor-network operator: an operator `TensorNetwork` together with the map between its
6-
# bra-side and ket-side physical names. The tensor-network analogue of a `NamedDimsArrays`
7-
# operator, with `state` the underlying operator network (mirroring `NamedDimsArrays.state`
8-
# on a `NamedDimsOperator`) and the tensor-network interface forwarded to it.
9-
struct TensorNetworkOperator{V, VD, State, SiteMap} <: AbstractTensorNetwork{V, VD}
5+
# A tensor-network operator: an operator `ITensorNetwork` together with the map between its
6+
# bra-side and ket-side physical names. The tensor-network analogue of an ITensor operator,
7+
# with `state` the underlying operator network (mirroring `ITensorBase.state` on an
8+
# `ITensorOperator`) and the tensor-network interface forwarded to it.
9+
struct TensorNetworkOperator{V, VD, State, SiteMap} <: AbstractITensorNetwork{V, VD}
1010
state::State
1111
site_index_map::SiteMap
1212
end
1313

14-
NDA.state(o::TensorNetworkOperator) = o.state
14+
ITB.state(o::TensorNetworkOperator) = o.state
1515
site_index_map(o::TensorNetworkOperator) = o.site_index_map
1616

17-
DataGraphs.underlying_graph(o::TensorNetworkOperator) = underlying_graph(NDA.state(o))
17+
DataGraphs.underlying_graph(o::TensorNetworkOperator) = underlying_graph(ITB.state(o))
1818
function DataGraphs.is_vertex_assigned(o::TensorNetworkOperator, v)
19-
return DataGraphs.is_vertex_assigned(NDA.state(o), v)
19+
return DataGraphs.is_vertex_assigned(ITB.state(o), v)
2020
end
2121
function DataGraphs.is_edge_assigned(o::TensorNetworkOperator, e)
22-
return DataGraphs.is_edge_assigned(NDA.state(o), e)
22+
return DataGraphs.is_edge_assigned(ITB.state(o), e)
2323
end
2424
function DataGraphs.get_vertex_data(o::TensorNetworkOperator, v)
25-
return DataGraphs.get_vertex_data(NDA.state(o), v)
25+
return DataGraphs.get_vertex_data(ITB.state(o), v)
2626
end
2727

2828
function Base.copy(o::TensorNetworkOperator)
29-
return TensorNetworkOperator(copy(NDA.state(o)), site_index_map(o))
29+
return TensorNetworkOperator(copy(ITB.state(o)), site_index_map(o))
3030
end
3131

3232
function TensorNetworkOperator(state, site_index_map)

test/test_aqua.jl

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,5 @@ using ITensorNetworksNext: ITensorNetworksNext
33
using Test: @testset
44

55
@testset "Code quality (Aqua.jl)" begin
6-
# Piracy is checked separately as `broken`: `dmrg.jl` temporarily pirates a few
7-
# `VectorInterface` methods on `AbstractNamedDimsArray` (needed by `KrylovKit.eigsolve`).
8-
# These are slated to move into `NamedDimsArrays`; drop the `broken` marker once they do.
9-
Aqua.test_all(ITensorNetworksNext; persistent_tasks = false, piracies = false)
10-
Aqua.test_piracies(ITensorNetworksNext; broken = true)
6+
Aqua.test_all(ITensorNetworksNext; persistent_tasks = false)
117
end

test/test_dmrg.jl

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
using Graphs: add_edge!, edges, vertices
2-
using ITensorBase: Index
3-
using ITensorNetworksNext: TensorNetwork, TensorNetworkOperator, dmrg, insertlink!
2+
using ITensorBase: Index, name, nameddims, setname, uniquename
3+
using ITensorNetworksNext: ITensorNetwork, TensorNetworkOperator, dmrg, insertlink!
44
using LinearAlgebra: eigen
55
using MatrixAlgebraKit: truncrank
6-
using NamedDimsArrays: name, nameddims, randname, setname
76
using NamedGraphs.NamedGraphGenerators: named_path_graph
87
using NamedGraphs: NamedGraph
98
using StableRNGs: StableRNG
@@ -34,7 +33,7 @@ function tfim_path_operator(N, sites, sitemap; J, h)
3433
verts = collect(1:N)
3534
bond_edges = [(verts[i], verts[i + 1]) for i in 1:(N - 1)]
3635
bonds = Dict(
37-
e => setname(Index(Base.OneTo(3)), randname(name(sites[verts[1]])))
36+
e => setname(Index(Base.OneTo(3)), uniquename(name(sites[verts[1]])))
3837
for e in bond_edges
3938
)
4039
W = ising_mpo_tensor(; J, h)
@@ -58,7 +57,7 @@ function tfim_path_operator(N, sites, sitemap; J, h)
5857
W, (name(bonds[bond_edges[left]]), name(bonds[bond_edges[right]]), k, b)
5958
)
6059
end
61-
tn = TensorNetwork(g) do v
60+
tn = ITensorNetwork(g) do v
6261
return tensor(v)
6362
end
6463
return TensorNetworkOperator(tn, sitemap)
@@ -83,7 +82,7 @@ function tfim_star_operator(sites, sitemap; J, h)
8382
add_edge!(g, 1, v)
8483
end
8584
bonds = Dict(
86-
v => setname(Index(Base.OneTo(3)), randname(name(sites[1]))) for v in leaves
85+
v => setname(Index(Base.OneTo(3)), uniquename(name(sites[1]))) for v in leaves
8786
)
8887
# Center tensor: one bond per leaf, with `-hX` once and `-JZ`/`I` on each leaf channel.
8988
center = zeros(3, 3, 3, 2, 2)
@@ -105,7 +104,7 @@ function tfim_star_operator(sites, sitemap; J, h)
105104
end
106105
tensors =
107106
Dict(1 => nameddims(center, center_names), (v => leaf_tensor(v) for v in leaves)...)
108-
tn = TensorNetwork(g) do v
107+
tn = ITensorNetwork(g) do v
109108
return tensors[v]
110109
end
111110
return TensorNetworkOperator(tn, sitemap)
@@ -124,7 +123,7 @@ end
124123

125124
function random_ket(rng, g)
126125
sites = Dict(v => Index(Base.OneTo(2)) for v in vertices(g))
127-
ket = TensorNetwork(NamedGraph(collect(vertices(g)))) do v
126+
ket = ITensorNetwork(NamedGraph(collect(vertices(g)))) do v
128127
return randn(rng, Float64, (sites[v],))
129128
end
130129
for e in edges(g)
@@ -138,7 +137,7 @@ end
138137
rng = StableRNG(8)
139138
g = named_path_graph(N)
140139
ket0, sites = random_ket(rng, g)
141-
sitemap = Dict(name(sites[v]) => randname(name(sites[v])) for v in vertices(g))
140+
sitemap = Dict(name(sites[v]) => uniquename(name(sites[v])) for v in vertices(g))
142141
operator = tfim_path_operator(N, sites, sitemap; J = 1.0, h = 0.7)
143142
exact = minimum(eigen(tfim_path_dense(N; J = 1.0, h = 0.7)).values)
144143

@@ -151,7 +150,7 @@ end
151150
rng = StableRNG(8)
152151
g = named_path_graph(N)
153152
ket0, sites = random_ket(rng, g)
154-
sitemap = Dict(name(sites[v]) => randname(name(sites[v])) for v in vertices(g))
153+
sitemap = Dict(name(sites[v]) => uniquename(name(sites[v])) for v in vertices(g))
155154
operator = tfim_path_operator(N, sites, sitemap; J = 1.0, h = 0.7)
156155
exact = minimum(eigen(tfim_path_dense(N; J = 1.0, h = 0.7)).values)
157156

@@ -165,7 +164,7 @@ end
165164
rng = StableRNG(8)
166165
g = named_path_graph(N)
167166
ket0, sites = random_ket(rng, g)
168-
sitemap = Dict(name(sites[v]) => randname(name(sites[v])) for v in vertices(g))
167+
sitemap = Dict(name(sites[v]) => uniquename(name(sites[v])) for v in vertices(g))
169168
operator = tfim_path_operator(N, sites, sitemap; J = 1.0, h = 0.7)
170169
exact = minimum(eigen(tfim_path_dense(N; J = 1.0, h = 0.7)).values)
171170

@@ -180,7 +179,7 @@ end
180179
rng = StableRNG(8)
181180
g = named_path_graph(N)
182181
ket0, sites = random_ket(rng, g)
183-
sitemap = Dict(name(sites[v]) => randname(name(sites[v])) for v in vertices(g))
182+
sitemap = Dict(name(sites[v]) => uniquename(name(sites[v])) for v in vertices(g))
184183
operator = tfim_path_operator(N, sites, sitemap; J = 1.0, h = 0.7)
185184
exact = minimum(eigen(tfim_path_dense(N; J = 1.0, h = 0.7)).values)
186185

@@ -197,7 +196,7 @@ end
197196
add_edge!(g, 1, v)
198197
end
199198
ket0, sites = random_ket(rng, g)
200-
sitemap = Dict(name(sites[v]) => randname(name(sites[v])) for v in vertices(g))
199+
sitemap = Dict(name(sites[v]) => uniquename(name(sites[v])) for v in vertices(g))
201200
operator = tfim_star_operator(sites, sitemap; J = 1.0, h = 0.6)
202201
exact = minimum(eigen(tfim_star_dense(; J = 1.0, h = 0.6)).values)
203202

@@ -209,7 +208,7 @@ end
209208
rng = StableRNG(8)
210209
g = named_path_graph(4)
211210
ket0, sites = random_ket(rng, g)
212-
sitemap = Dict(name(sites[v]) => randname(name(sites[v])) for v in vertices(g))
211+
sitemap = Dict(name(sites[v]) => uniquename(name(sites[v])) for v in vertices(g))
213212
operator = tfim_path_operator(4, sites, sitemap; J = 1.0, h = 0.7)
214213
@test_throws ArgumentError dmrg(operator, ket0)
215214
end

0 commit comments

Comments
 (0)