tenet.serialize¶
save and load for symmetric tensors.
tenet.serialize ¶
save / load a SymmetricTensor over a single .npz.
The file is a JSON header describing the legs plus one .npy member per block.
Nothing derived is written: block_order, block_shape and every fusion tree are
pure functions of legs, so putting them on disk would create a second source of
truth. num_blocks is in the header only as a read count, checked against the
freshly derived value.
NumPy is correct here and only here for the reason it is in tenet.ops.dense: the
file format is NumPy's. Blocks arrive through ar.to_numpy and leave through the
ordinary constructor, so no backend is imported and a JAX- or torch-backed tensor saves
fine. load always returns NumPy blocks; load(path).to_backend("jax") is the
documented restore, because a device placement is not a property of a tensor.
The SU(2), SU(N) and fZ2 gauge fingerprints are written and verified on load: block coefficients are only meaningful against the CG / F / R conventions that produced them, and a file outlives the process whose provider identity pinned those conventions — a mismatch would be silently wrong in a way no shape check catches.
FORMAT_VERSION
module-attribute
¶
On-disk format version. Files are not guaranteed readable across tenet
versions before 1.0; a future version is refused loudly rather than misread.
save ¶
save(
t: SymmetricTensor,
path: str | PathLike,
*,
compress: bool = False,
) -> None
Write t to path as a single .npz: a JSON header plus one array per block.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
SymmetricTensor
|
The tensor written; any backend. Blocks are converted with
|
required |
path
|
str or PathLike
|
The destination file. |
required |
compress
|
bool
|
|
False
|
Raises:
| Type | Description |
|---|---|
TypeError
|
If a |
ValueError
|
If a leg's provider is not one of the serializable kinds. |
Examples:
>>> import os, tempfile
>>> import tenet
>>> from tenet import IN, OUT, GradedSpace, Leg, SymmetricTensor
>>> from tenet.symmetry import U1, U1Sector
>>> V = GradedSpace.new(U1, {U1Sector(0): 1, U1Sector(1): 1})
>>> t = SymmetricTensor.random((Leg(V, OUT), Leg(V, IN)), seed=0)
>>> with tempfile.TemporaryDirectory() as d:
... tenet.save(t, os.path.join(d, "t.npz"))
... t2 = tenet.load(os.path.join(d, "t.npz"))
>>> t2.structure == t.structure
True
>>> bool(tenet.allclose(t2, t))
True
Notes
A saved tensor loads back as NumPy — load(...).to_backend("jax") is the
documented restore, because a device placement is not a property of the tensor.
compress=False by default: reduced blocks are dense float arrays that do
not compress well, so paying zlib on every checkpoint buys nothing. The
zip container costs a constant overhead — a 4-block SU(2) tensor with 504
bytes of block data writes a 2282-byte file — which is not a bug.
load ¶
load(path: str | PathLike) -> SymmetricTensor
Read a tensor written by save. NumPy blocks; structure exactly equal.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str or PathLike
|
The |
required |
Returns:
| Type | Description |
|---|---|
SymmetricTensor
|
The saved tensor, blocks NumPy-backed; |
Raises:
| Type | Description |
|---|---|
ValueError
|
If — for SU(2), SU(N) or fZ2 — the file's gauge fingerprint is not the
running build's: block coefficients are only meaningful against the
CG / F / R conventions that produced them, so a gauge-mismatched file
is refused rather than silently misread. The one exception is the SU(2)
fingerprint listed in |
KeyError
|
For an unknown provider kind. |
Examples:
See save for the round trip.
Notes
Block count and per-block shape are validated by
SymmetricTensor.__post_init__, unmodified.