Skip to content

Add ITensorNetworkOperator and QuadraticFormNetwork data structures - #195

Open
jack-dunham wants to merge 28 commits into
mainfrom
jd/quadraticform
Open

jack-dunham wants to merge 28 commits into
mainfrom
jd/quadraticform

Conversation

@jack-dunham

@jack-dunham jack-dunham commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

Add QuadraticFormNetwork and ITensorNetworkOperator

This adds QuadraticFormNetwork, a lazy three-layer network (ket, operator, bra) for computing ⟨ψ|O|ψ⟩, and a shared AbstractBilinearFormNetwork supertype that it and NormNetwork both use.

  • ITensorNetworkOperator: the network version of ITensorOperator. It's a tensor network plus an output→input pairing of its dangling indices, built with operator(tn, outputnames, inputnames). Each pair has to sit on one vertex, and op[v] returns that vertex's tensor as an ITensorOperator.
  • QuadraticFormNetwork(ψ, O) / quadraticformnetwork(ψ, O): wraps a state and an operator. The operator's inputs connect to the ket, and its outputs are renamed so they connect to the bra. Like NormNetwork, indexing it throws an error. factor_tensors hands its three layers to BP and prod_tensors as separate operands.
  • AbstractBilinearFormNetwork: graph forwarding, braname, kettensor, bratensor and nametype are now defined once here. A subtype only needs ketnetwork and branamemap. NormNetwork is now a subtype.
  • KetView is replaced by ketnetwork(bn), and BraView is now created with branetwork(bn), and works for any bilinear-form network.
  • indmap is removed (it was unused).
  • NormNetwork moved to src/bilinearforms/ along with the new files.

@ITensorBot

ITensorBot commented Sep 16, 2026 •

Copy link
Copy Markdown
Member

Your PR no longer requires formatting changes. Thank you for your contribution!

@codecov

codecov Bot commented Sep 16, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.05263% with 17 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.40%. Comparing base (b98db7a) to head (608bde6).

Files with missing lines Patch % Lines
src/bilinearforms/abstractbilinearformnetwork.jl 86.00% 7 Missing ⚠️
src/itensornetworkoperator.jl 88.63% 5 Missing ⚠️
src/abstracttensornetwork.jl 85.00% 3 Missing ⚠️
src/ITensorNetworkGenerators/ising_network.jl 0.00% 1 Missing ⚠️
src/bilinearforms/quadraticformnetwork.jl 97.43% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #195      +/-   ##
==========================================
+ Coverage   88.81%   89.40%   +0.59%     
==========================================
  Files          17       19       +2     
  Lines         724      840     +116     
==========================================
+ Hits          643      751     +108     
- Misses         81       89       +8     
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.

Comment thread src/itensornetworkoperator.jl Outdated
Comment thread src/bilinearforms/bilinearformnetworkview.jl Outdated
@jack-dunham
jack-dunham force-pushed the jd/quadraticform branch 3 times, most recently from 3457ed5 to 2903a18 Compare September 29, 2026 14:48
jack-dunham and others added 15 commits October 8, 2026 11:08
`QuadraticFormNetwork` represents `⟨tn|op|tn⟩` lazily, with an operator layer
between the bra and ket layers. A ket index name is renamed in the bra layer
when it is a link name or appears in the operator's `inputnames`, so a ket index
the operator does not act on still contracts straight onto the bra.
`operatortensor` renames each operator output name to the bra name of the input
name it is paired with, leaving the input names to meet the ket. The constructor
throws unless `vertices(operator)` and `vertices(ket)` are set-equal.

`AbstractBilinearFormNetwork` is the new supertype of `NormNetwork` and
`QuadraticFormNetwork`. A subtype implements `braname`, `kettensor` and, where it
has an operator layer, `operatortensor`; `bratensor`, `conj_bratensor` and
`indmap` are defined once in terms of those, so the two concrete files now share
them instead of each carrying a copy. `AbstractBilinearFormNetworkView` does the
same for `KetView`, `BraView` and the new `OperatorView`: each implements
`Base.parent` and `get_vertex_data`, and takes its graph structure, assignment
and mutability from the parent network.

`test/test_quadraticformnetwork.jl` covers the name map, the three views, and
three contractions: an identity operator layer reproduces
`contract_network(NormNetwork(tn))`, a random on-site product operator matches a
dense `⟨ψ|O|ψ⟩` reference, and a `2I` layer on three vertices scales the norm by
8.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The five files are byte-identical at their new paths; only the `include` paths in
`src/ITensorNetworksNext.jl` change. `src/normnetworkview.jl` becomes
`bilinearformnetworkview.jl`, since it now holds the views of both `NormNetwork`
and `QuadraticFormNetwork`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`OperatorView` now accepts any `AbstractBilinearFormNetwork` rather than only a
`QuadraticFormNetwork`, and a second constructor throws an `ArgumentError` for a
`NormNetwork`, which reports the reason rather than surfacing a `MethodError`.

A `NormNetwork` maps every site index name to itself in its bra layer, so an
identity operator between the layers would need its output name to equal its
input name, and `ITensor` rejects that: "Dimension names must be distinct".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`encoded_vertex`, `decoded_vertex` and `encoded_graph` move from a
`for View in (:KetView, :BraView)` loop in `bilinearformnetworkview.jl` to
`AbstractBilinearFormNetworkView` in `abstractbilinearformnetworkview.jl`,
replacing the `vertex_positions`/`ordered_vertices`/`position_graph` methods that
NamedGraphs 0.13 removed. `OperatorView` is now covered as well, as it was by the
methods being replaced. The loop also `@eval`ed against `BraView` above that
type's own definition, so including the file threw `UndefVarError: BraView`.

`QuadraticFormNetwork` gets the same rename; it still defined the three removed
names, so the package did not load. `test_quadraticformnetwork.jl` takes
`incident_edges`, `named_grid` and `named_path_graph` from `NamedGraphs` itself,
since the `GraphsExtensions` and `NamedGraphGenerators` submodules are gone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`ITensorNetworkOperator` is the network equivalent of `ITensorBase.ITensorOperator`:
a tensor network together with a positional pairing of its dangling index names,
`outputnames[i]` with `inputnames[i]`. The constructor, reached through
`ITensorBase.operator(tn, out, in)`, throws an `ArgumentError` when the two name
lists differ in length and when a named index is not dangling, since
`dimnamevertices` must return exactly one vertex for an operator leg.

A pair may straddle two vertices, as it does for a swap or a translation, so
`getindex` wraps each vertex tensor as an `ITensorOperator` carrying only the pairs
whose two halves both sit on that vertex; the remaining legs are dangling on the
wrapper, which `NamedTensorOperator` permits for names in neither list. The wrapper
is built even when no pair is local, so `eltype` is `NamedTensorOperator{I, T}` for
every vertex.

`QuadraticFormNetwork` takes one as its operator layer. Its `acted` set is now
`Set{I}(inputnames(operator))` rather than a loop over per-vertex input names, and
`operatortensor` reads the network-level pairing: reading `qf.operator[vertex]`
would skip a crossing pair's output leg, which is dangling on that wrapper, and the
bra layer would not meet it. `operatortensor` therefore returns a plain tensor and
`get_vertex_data` drops its `state` call.

`supportof` gains a method for the new type. Its body moves to
`supportof_dimnames(tn, opnames)` in `abstracttensornetwork.jl` so both methods
share it; a `Union` signature is not possible there because
`ITensorNetworkOperator` is not yet defined at that point in the include order.

`replacedimnames` is not implemented for the new type: `ITensorNetwork` has no
`replacedimnames` method to delegate to.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…etwork`

`ketnetwork` and `operatornetwork` return the stored ket network and `ITensorNetworkOperator`.
`branetwork` defaults to a `BraView`, since a bilinear-form network does not store its bra layer.
The belief propagation code for `NormNetwork` now reads its link names through these accessors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ds on `BraView`

`BraView` was the only remaining subtype, so it now subtypes `AbstractITensorNetwork`
directly and carries the graph, encoding, assignment and mutability forwarding itself.
The include order places `bilinearformnetworkview.jl` first, since the forwarding methods
now name `BraView`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pure move: the contents of `bilinearformnetworkview.jl` and
`abstractbilinearformnetworkview.jl` are appended verbatim, and their `using` lines are
merged into the existing imports. `src/bilinearforms/` now holds the interface, `NormNetwork`
and `QuadraticFormNetwork`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… `operatortensor`

`ITensorNetworkOperator` now throws when a pair's output and input sit on different vertices,
so each vertex operator carries its full pairing, and `operatortensor` reads it from
`qf.operator[vertex]` rather than from the network-wide name vectors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The output and input name vectors are replaced by one map from each output name to its input
name, so `getindex` looks up a vertex's pairs by name instead of scanning every pair. A repeated
output name now throws an `ArgumentError`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…map`

Graph forwarding, the ket part of `is_vertex_assigned`, `braname` and `kettensor` are defined
once on `AbstractBilinearFormNetwork` through `ketnetwork` and a new `branamemap` accessor,
and both constructors build the name map with `select_branames`. The unused `indmap` is
removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`AbstractBilinearFormNetwork{T, V, I}` carries the index name type `I`; `nametype` returns it,
as it does for `ITensorNetwork`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…et layer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… error

A `QuadraticFormNetwork`'s factor at a vertex is its ket, operator and bra layers as separate
operands. Indexing and `eltype` now throw, as they do for `NormNetwork`, since the lazy
triple-layer product they returned cannot be contracted by `prod_tensors`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jack-dunham
jack-dunham marked this pull request as ready for review October 8, 2026 15:32
Jack Dunham and others added 3 commits October 8, 2026 12:08
`inds(tn, v)` and `names(tn, v)` give the indices and names of the tensor at vertex `v`,
defaulting to `inds(tn[v])`. The generic link and site functions, `dimnamevertices`,
`has_dimname` and `insertlink!` read vertex metadata through them instead of indexing the
vertex. Indexing a `BraView` builds the bra tensor through `bratensor`, whose `conj` broadcast
allocates a conjugated copy of the ket tensor; `BraView` now overloads `inds(tn, v)` with the
conjugated indices of `conj_bratensor`, which only renames. This removes the tensor-sized copies
from `similar_message_environment` and from the bra names read after every bilinear-form
message update.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The generic method returned a `Vector` in vertex order, while the `ITensorNetwork` method
returned the network's stored `Set` itself, so a caller mutating the result changed
`tn.dimname_vertices`. Both now return a new `Set{vertextype(tn)}`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Defining them on Graphs' `AbstractGraph` added methods of an ITensorBase function to a type
neither package owns, which Aqua's piracy check rejects. Every caller passes an
`AbstractITensorNetwork`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Jack Dunham and others added 2 commits October 8, 2026 15:26
`select_branames` both chose the names that get a separate bra name and restricted `map`
to them. `linknames(tn)` now gives the link names of a whole network, with a fast method
for `ITensorNetwork`, and the constructors restrict `map` with Dictionaries' `getindices`:
`NormNetwork` to the link names, and `QuadraticFormNetwork` to the link names and the
operator's input names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`NormNetwork` and `QuadraticFormNetwork` kept bra names only for the links, so on a
network that is part of a larger one, a bond to a vertex outside it kept its ket name in
the bra layer. Message updates then left that bond's legs uncontracted. Any index name of
the ket layer in the map now gets its own bra name; the one-argument constructors map only
the links (and operator inputs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Jack Dunham and others added 8 commits October 15, 2026 14:03
Add `derivedname(name, key)`, which replaces the UUID of an `IndexName` with
`uuid5(uuid(name), string(key))`, so every process derives the same bra name from the same
ket name. `NormNetwork([f], tn, [linknames])` and
`QuadraticFormNetwork([f], ket, op, [linknames])` map each link name (and, for the quadratic
form, each operator input name) to `f(name)`, with `f` defaulting to
`name -> derivedname(name, "bra")`. The inner constructors store the given `Dictionary` as is
and `check_input` throws if two bra names coincide or a bra name is already an index name of
the ket or operator layer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`derivedname(name, key)` now adds `key` as a bare-label tag, so a default bra name carries a
`bra` tag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…f a literal `\\`

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Network`

`efe6d1d` stored the name map as given, so a map without a link was accepted and `braname`
returned the ket name for it. `check_input` now throws if a link name, or for a quadratic form
an operator input name, is not a key of the map; other keys are allowed. The checks shared by
the two constructors move into `check_branamemap`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`map(f, ::Indices)` keeps the inferred return type of `f`, so a non-inferable `f` gave a
`Dictionary{I, Any}` and no inner constructor method matched. `narrow_map` collects the
values with a comprehension, which takes the type of the values returned.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

2 participants