Skip to content

Refactor TensorNetwork type; add NormNetwork struct. #119

Draft
jack-dunham wants to merge 102 commits into
mainfrom
jd/tensornetwork-refactor
Draft

Refactor TensorNetwork type; add NormNetwork struct. #119
jack-dunham wants to merge 102 commits into
mainfrom
jd/tensornetwork-refactor

Conversation

@jack-dunham

Copy link
Copy Markdown
Contributor

This PR refactors the TensorNetwork data type to include a reverse index map, and to be stricter about it's construction and how it's tensors can be set.

This PR also adds NormNetwork type, as a wrapper around a TensorNetwork, to represent the norm of a tensor network.

JoeyT1994 and others added 30 commits January 6, 2026 09:55
Introduce `BeliefPropagationProblem` wrapper to hold the cache and the
error `diff` field.

Also simplifies some kwargs wrangling.
Also includes some fixes to the way `TensorNetwork` types are
constructed based on index structure.
…instead of trying to operate on existing graphs

The reason for this is:
- One only cares about the edges of the input graph
- A simple graph cannot be used as it "forgets" its edge names resulting
in recursion
- As shown with `TensorNetwork`, removing edges may not always be
defined.
This was caused by the change to the `cache` being backed by a directed
graph.
@codecov

codecov Bot commented Jun 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 72.65625% with 70 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.50%. Comparing base (dd886af) to head (0edd3bc).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
src/abstracttensornetwork.jl 44.82% 16 Missing ⚠️
src/tensornetwork.jl 83.67% 16 Missing ⚠️
src/beliefpropagation/messagecache.jl 65.11% 15 Missing ⚠️
src/normnetwork.jl 65.71% 12 Missing ⚠️
src/normnetworkview.jl 40.00% 9 Missing ⚠️
src/beliefpropagation/normnetwork.jl 92.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #119      +/-   ##
==========================================
- Coverage   80.35%   78.50%   -1.85%     
==========================================
  Files          13       13              
  Lines         738      670      -68     
==========================================
- Hits          593      526      -67     
+ Misses        145      144       -1     
Flag Coverage Δ
docs 0.00% <0.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ITensorBot

ITensorBot commented Jun 8, 2026

Copy link
Copy Markdown
Member

Your PR requires formatting changes to meet the project's style guidelines.
Please run the ITensorFormatter to apply these changes.

Click here to view the suggested changes.
diff --git a/src/normnetwork.jl b/src/normnetwork.jl
index 881683c..f165e5f 100644
--- a/src/normnetwork.jl
+++ b/src/normnetwork.jl
@@ -25,7 +25,9 @@ end
 
 Base.eltype(::Type{<:NormNetwork{T}}) where {T} = LazyITensor{eltype(T), T}
 
-NormNetwork(tn::ITensorNetwork) = NormNetwork(tn, map(uniquename, keys(tn.dimname_vertices)))
+function NormNetwork(tn::ITensorNetwork)
+    return NormNetwork(tn, map(uniquename, keys(tn.dimname_vertices)))
+end
 
 # ====================================== Graphs.jl ======================================= #
 
diff --git a/test/test_apply_operator.jl b/test/test_apply_operator.jl
index a20d2b1..1d4d623 100644
--- a/test/test_apply_operator.jl
+++ b/test/test_apply_operator.jl
@@ -3,7 +3,7 @@ import TensorAlgebra as TA
 using GradedArrays: U1, gradedrange
 using Graphs: dst, edges, src, vertices
 using ITensorBase: Index, name, named, operator, setname, uniquename
-using ITensorNetworksNext: NormNetwork, ITensorNetwork, apply_operator, apply_operators,
+using ITensorNetworksNext: ITensorNetwork, NormNetwork, apply_operator, apply_operators,
     beliefpropagation, insertlink!, message_environment, tensornetwork
 using MatrixAlgebraKit: truncrank
 using NamedGraphs.NamedGraphGenerators: named_cycle_graph, named_path_graph
diff --git a/test/test_beliefpropagation.jl b/test/test_beliefpropagation.jl
index e3d6ddb..3ecf8ee 100644
--- a/test/test_beliefpropagation.jl
+++ b/test/test_beliefpropagation.jl
@@ -3,8 +3,8 @@ using DataGraphs: DataGraphs, DataGraph, edge_data, edge_data_type
 using Dictionaries: Dictionary, dictionary, set!
 using Graphs: AbstractGraph, dst, edges, has_edge, src, vertices
 using ITensorBase: ITensor, Index, inds, name, noprime, prime
-using ITensorNetworksNext: ITensorNetworksNext, MessageCache, StopWhenConverged,
-    ITensorNetwork, bethe_free_energy, edge_scalar, incoming_messages, linkinds,
+using ITensorNetworksNext: ITensorNetworksNext, ITensorNetwork, MessageCache,
+    StopWhenConverged, bethe_free_energy, edge_scalar, incoming_messages, linkinds,
     messagecache, region_scalar, subgraph, tensornetwork, vertex_scalar, vertex_scalars
 using LinearAlgebra: LinearAlgebra
 using NamedGraphs.GraphsExtensions: all_edges, arranged_edges, incident_edges, vertextype
diff --git a/test/test_contract_network.jl b/test/test_contract_network.jl
index d8cbdf6..a6cdb30 100644
--- a/test/test_contract_network.jl
+++ b/test/test_contract_network.jl
@@ -1,8 +1,7 @@
 using Graphs: edges, vertices
-using ITensorNetworksNext: contract_network, linkinds, siteinds, tensornetwork
 using ITensorBase: Greedy, Index, Optimal
-using ITensorNetworksNext:
-    Exact, LeftAssociative, ITensorNetwork, contract_network, linkinds, siteinds, tensornetwork
+using ITensorNetworksNext: Exact, ITensorNetwork, LeftAssociative, contract_network,
+    linkinds, siteinds, tensornetwork
 using NamedGraphs.GraphsExtensions: arranged_edges, incident_edges
 using NamedGraphs.NamedGraphGenerators: named_grid
 using TensorOperations: TensorOperations

@jack-dunham jack-dunham force-pushed the jd/tensornetwork-refactor branch from 1310cc2 to 7c51b20 Compare June 9, 2026 20:34
Comment thread src/normnetwork.jl
Comment on lines +41 to +43
B = conjbra(nn, vertex)
# TODO: implement and use a lazy `conj` via `LazyNamedDimsArrays` here?
return lazy(A) * lazy(conj(B))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can define bra(nn, vertex) = conj(conjbra(nn, vertex))? Agreed we should probably define a lazy conj, it is something that has come up a few places, but eager is ok for now.

Comment thread src/normnetwork.jl Outdated
ket(nn::NormNetwork, vertex) = nn.tensornetwork[vertex]
conjbra(nn::NormNetwork, vertex) = replacedimnames(n -> namemap(nn, n), ket(nn, vertex))

lazy_norm(tn::TensorNetwork) = NormNetwork(tn)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we were calling this norm_network in ITensorNetworksNext. I kind of prefer that name since it indicates what kind of lazy representation it is, i.e. one could imagine lazy_norm might output a lazy expression for the norm. I think norm_network is a bit more descriptive.

return tn
end
# Return the vertices associated with an index.
function indsites(tn::AbstractGraph, ind)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe dimnamevertices?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess my motivation for this name was the existing function names for the tensor network type:

linkinds
siteinds

I.e. indsites is the "inverse" of siteinds. Happy to revisit these names, but we should choose something consistent.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One contrast to make is that this function really just using the dimension name to look up with vertices, while siteinds is indicating that it is outputting indices/named axes. And I agree that it is slightly unfortunate mixing graph terminology of vertices/edges with more physics terminology of sites/links, I went back and forth about that but I'm not sure we need to overoptimize for consistency with linkinds/siteinds naming here.

for e in edges(graph)
show(io, mime, e)
println(io)
function has_indname(tn::AbstractGraph, name)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

has_dimname?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above comment on this.

Comment thread src/normnetwork.jl Outdated
index_map = Dictionary{I, I}()
for (name, vertices) in pairs(tn.index_locations)
if length(vertices) == 2
insert!(index_map, name, randname(name))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be nice to have an interface where we can specify the ket names (say if you have a set of messages and want a norm network that matches the names of the messages).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. We can have:

### Inner constructor:
    function NormNetwork(tn::TensorNetwork{T, V, I}, map::Dictionary{I, I}) where {T, V, I}
        namemap = Dictionary{I, I}()
        for (name, vertices) in pairs(tn.index_locations)
            if length(vertices) == 2
                insert!(namemap, name, map[name])
            end
        end
        return new{T, V, I}(tn, namemap)
    end
###

NormNetwork(tn::TensorNetwork) = NormNetwork(tn, map(randname, keys(tn.index_locations)))

Comment thread src/tensornetwork.jl
function TensorNetwork(f::Base.Callable, graph::AbstractGraph)
return TensorNetwork(graph, Dictionary(map(f, vertices(graph))))
end
for ind in dimnames(tensor)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The terminology is definitely kind of tricky. I think if something is just a name, we should refer to it as dimname or name. In my mind, ind would refer to a named axis, though I've gone back and forth about whether we should still use the ind/inds terminology from ITensors.jl or embrace the axis/axes terminology from Base Julia...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, it should definitely be name (I was going back and forth between storing the actual index or the name, so I think this is left over from that).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've gone back and forth about the dimnames vs. inds question as well, and the approach if been trying for in ITensorBase/ITensorNetworksNext is actually a fairly big shift from ITensors/ITensorMPS/ITensorNetworks: I think we were overusing inds (i.e. overusing passing around both the name and the range/space information) while I think it is clearer in many places to just to use the name as an input/output. I.e. I'd like to be more precise in ITensorBase/ITensorNetworksNext about which functions really just need inputs and outputs of dimension names without the space. The issue was that in ITensors/ITensorMPS/ITensorNetworks, we used inds in places where really only the name was needed and being used, so it become unclear when the space was being ignore/implicitly dropped (such as in replaceinds).

Comment thread src/tensornetwork.jl Outdated

struct TensorNetwork{T, V, I} <: AbstractTensorNetwork{T, V}
tensors::Dictionary{V, T}
index_locations::Dictionary{I, Set{V}}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a better name would be dimname_vertices.

if VERSION >= v"1.11.0-DEV.469"
eval(
Meta.parse(
"public apply_operator, apply_operators, beliefpropagation_normnetwork, identity_norm_message_env, normnetwork, norm_message_env, ones_norm_message_env, rand_norm_message_env, randn_norm_message_env, similar_norm_message_env"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we wanted to remove all of these, i.e. apply_operator[s] should remain.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants