This page describes the public Python API you are most likely to use when you want to keep a backend-independent tensor-network design and generate backend code from it. For the data model fields themselves, see data-models.md.
- Main Imports
- Launch the Editor
- Generate Code
- Render Static Figures
- Save and Load Designs
- Build Specs in Python
- Validate, Lint, Analyze, Canonicalize, and Diff
- Templates
- Subnetwork Helpers
- Extension Hooks
- Errors
- Small Complete Example
The package exposes the main functions and models at the top level:
from tensor_network_editor import (
EngineName,
EditorThemeName,
EditorUiMode,
NetworkBuilder,
NetworkSpec,
PythonLoadOptions,
DotRenderOptions,
TensorDataMode,
TensorDataSpec,
SvgRenderOptions,
TikzRenderOptions,
TensorCollectionFormat,
ValidationIssue,
analyze_contraction,
analyze_spec,
build_template_spec,
canonicalize_spec,
diff_specs,
generate_code,
lint_spec,
list_template_names,
load_python_spec,
load_spec,
open_editor,
render_spec_dot,
render_spec_png,
render_spec_svg,
render_spec_tikz,
save_spec,
semantic_diff_specs,
validate_spec,
)Most workflows only need a few of these imports.
The documented imports above are the stable guided public surface. Explicit
public modules such as tensor_network_editor.editor,
tensor_network_editor.io, tensor_network_editor.models, and
tensor_network_editor.templates stay available when you want more structure
or advanced hooks.
Useful public modules:
| Module | Use it for |
|---|---|
tensor_network_editor.editor |
EditorLaunchOptions, EditorThemeName, EditorUiMode, and open_editor(...) |
tensor_network_editor.builder |
fluent NetworkBuilder, TensorHandle, and IndexHandle helpers |
tensor_network_editor.io |
JSON/Python loading, saving, serialize_spec(...), and SCHEMA_VERSION |
tensor_network_editor.models |
data classes, result models, enums, and periodic-mode types |
tensor_network_editor.rendering |
Matplotlib-backed SVG/PNG/PDF plus pure-Python TikZ and DOT rendering helpers |
tensor_network_editor.validation |
hard validation helpers |
tensor_network_editor.linting |
soft modeling diagnostics |
tensor_network_editor.analysis |
structural and contraction analysis |
tensor_network_editor.canonicalization |
stable ordering and optional deterministic ids |
tensor_network_editor.templates |
built-in templates and template registration |
tensor_network_editor.subnetworks |
extraction and insertion preparation for reusable fragments |
tensor_network_editor.codegen.registry |
advanced backend-generator registration |
Modules under tensor_network_editor.internal are implementation details. They
are used by the package itself and by tests, but they are not a stable
user-facing API.
Use open_editor(...) when you want a local editing session from Python.
from tensor_network_editor import EngineName
from tensor_network_editor.editor import EditorLaunchOptions, open_editor
def main() -> None:
result = open_editor(
options=EditorLaunchOptions(
default_engine=EngineName.EINSUM_NUMPY,
theme="light",
ui_mode="browser",
),
)
if result is None:
print("Editor cancelled.")
return
print(result.engine.value)
print(result.spec.name)
if result.codegen is not None:
print(result.codegen.code)Main parameters:
spec: preload an existingNetworkSpecoptions.default_engine: initial target backend shown in the editoroptions.default_collection_format: initial tensor collection layoutoptions.theme: initial color theme, one ofdark,light,contrast,colorblind, orshinyoptions.ui_mode: choosebrowser,pywebview, orserveroptions.open_browser: legacy browser/server compatibility flagoptions.host: local host address, default127.0.0.1options.port: local port, default0so the OS chooses oneoptions.print_code: print generated code after confirmationoptions.code_path: write generated code after confirmationoptions.template_catalog_path: optional per-project static template catalog pathoptions.subnetwork_catalog_path: optional per-project reusable-subnetwork catalog pathoptions.shared_subnetwork_catalog_path: optional shared reusable-subnetwork catalog path merged with the project catalog at runtimeoptions.draft_path: optional path for the recoverable local editor draft; leave it unset to use the project-local default under.tensor-network-editor/drafts/
Install the optional desktop extra before using options.ui_mode="pywebview":
python -m pip install "tensor-network-editor[desktop]"Return value:
Nonewhen the user cancelsEditorResultwhen the user confirms
EditorResult contains spec, engine, codegen, and confirmed.
Practical note:
- if project and shared reusable-subnetwork catalogs define the same entry name, the project entry shadows the shared one
Use generate_code(...) when you already have a NetworkSpec.
from tensor_network_editor import (
EngineName,
TensorCollectionFormat,
generate_code,
load_spec,
)
spec = load_spec("my_network.json")
result = generate_code(
spec,
engine=EngineName.QUIMB,
collection_format=TensorCollectionFormat.DICT,
output_path="generated_network.py",
)
print(result.engine.value)
print(result.code)
print(result.warnings)Useful behavior:
print_code=Trueprints the generated sourceoutput_path="..."writes the generated source to a fileexternal_data_base_path="..."anchors relative.npy/.npztensor-data paths before writing generated code; without it, the stored path text is used as-iscollection_formatcan beLIST,MATRIX, orDICT- a backend-specific export problem raises
CodeGenerationErrorfromtensor_network_editor.errors
If a saved contraction_plan exists, generated code follows that manual plan.
Complete plans emit a final result. Partial plans emit intermediate values
and a remaining_operands mapping.
Use render_spec_svg(...), render_spec_pdf(...), render_spec_tikz(...),
render_spec_dot(...), or render_spec_png(...) when you want a static figure
without opening the browser editor.
from tensor_network_editor import (
DotRenderOptions,
SvgRenderOptions,
TikzRenderOptions,
load_spec,
render_spec_dot,
render_spec_pdf,
render_spec_png,
render_spec_svg,
render_spec_tikz,
)
spec = load_spec("my_network.json")
svg = render_spec_svg(
spec,
options=SvgRenderOptions(padding=48.0),
output_path="figure.svg",
)
tikz = render_spec_tikz(
spec,
options=TikzRenderOptions(scale=0.02),
output_path="figure.tex",
)
dot = render_spec_dot(
spec,
options=DotRenderOptions(include_open_indices=True),
output_path="graph.dot",
)
png = render_spec_png(spec, output_path="figure.png")
pdf = render_spec_pdf(spec, output_path="figure.pdf")
print(svg[:80])SVG, PNG, and PDF export use the same Matplotlib-based academic figure renderer, so SVG keeps text as real SVG text nodes and PDF stays vectorial with selectable text in normal viewers. TikZ and DOT rendering are still pure Python and have no browser, Node, LaTeX, or Graphviz runtime dependency. The renderers validate the spec and draw tensors, open ports, pairwise edges, hyperedges, groups, and notes where the target format supports them.
Use save_spec(...) and load_spec(...) for the abstract JSON design.
from tensor_network_editor import load_spec, save_spec
spec = load_spec("my_network.json")
save_spec(spec, path="copy_of_my_network.json")Important details:
save_spec(...)validates before writingload_spec(...)accepts saved JSON designsload_spec(...)also accepts supported.pysources and autodetects one of the built-in Python import profiles:generated,quimb,tensornetwork, oreinsumload_python_spec(...)works when source is already in memory- both functions accept
python=PythonLoadOptions(...)when you want to lock the parser or import behavior explicitly PythonLoadOptions.source_profiledefaults toauto, or acceptsgenerated,quimb,tensornetwork, oreinsumwhen you want to pin the parser explicitlyPythonLoadOptions.import_mode="live"executes the source in a subprocess using the active Python interpreter, supports livequimbandtensornetworkobjects, and acceptsobject_name="..."when several compatible globals exist- Only use live import with local Python files you trust. Live import executes the file in a subprocess with the active Python environment, so trusted code can still read or write local files.
PythonLoadOptions.reconstruction_level="simple"rebuilds only the portable network structure: tensors, inferable connections, and portable tensor-data payloadsPythonLoadOptions.reconstruction_level="best_available"is currently only supported for the package's owngeneratedprofilePythonLoadOptions.reconstruction_level="auto"resolves tobest_availablefor thegeneratedprofile and tosimplefor external static profiles plus live imports- live import preserves tensor data when it can be lowered to a portable initializer or small real/complex literal payload, and otherwise drops that data with a warning
- when live import is requested for generated source and backend imports fail, the loader tries the static generated-source parser and returns a warning that names the fallback
When you build HyperedgeSpec values from Python,
hub_offset=CanvasPosition(...) stores the editor's draggable hub displacement
in the saved JSON. That offset is relative to the automatic hub center
computed from the endpoints, and older JSON payloads that predate this field
still load with a zero offset.
Round-trip from generated source:
from tensor_network_editor import (
EngineName,
generate_code,
load_python_spec,
)
result = generate_code(spec, engine=EngineName.EINSUM_NUMPY)
round_tripped_spec = load_python_spec(result.code)
print(round_tripped_spec.name)Explicit profile selection:
from tensor_network_editor import PythonLoadOptions, load_python_spec
spec = load_python_spec(
quimb_source,
python=PythonLoadOptions(source_profile="quimb"),
)Explicit live import from one named global:
from tensor_network_editor import PythonLoadOptions, load_spec
spec = load_spec(
"runtime_network.py",
python=PythonLoadOptions(
source_profile="quimb",
import_mode="live",
reconstruction_level="simple",
object_name="network",
),
)The Python importer is intentionally conservative. Supported generated exports
still provide the richest round-trip, including recovery of manual contraction
steps into ContractionPlanSpec.steps, so that is the only profile that
currently supports best_available. The external quimb, tensornetwork, and
einsum profiles only parse supported static AST shapes, and the live quimb
/ tensornetwork mode executes user code in a subprocess but still follows the
portable simple reconstruction contract. That means external and live imports
do not recover editor layout/groups/notes or rebuild manual contraction plans.
Editor-only view_snapshots are still reset to an empty list because Python
source does not carry scene layout. Hyperedges from supported generated exports
are reconstructed as HyperedgeSpec from the structured copy-tensor markers.
Linear, grid, and tree periodic generated Python emitted by current versions
embeds compact metadata so supported imports recover the editable periodic
payload. This is still not a general Python-to-network importer.
Use NetworkBuilder when you want to create normal-mode designs directly from
Python without writing tensor, index, and endpoint ids by hand.
from tensor_network_editor import NetworkBuilder
builder = NetworkBuilder("chain")
left = builder.tensor("A", position=(120.0, 160.0))
left.index("i", 2)
left.index("x", 3)
right = builder.tensor("B", position=(360.0, 160.0))
right.index("x", 3)
right.index("j", 4)
builder.connect(left["x"], right["x"], name="bond_x")
spec = builder.build()builder.build() validates the spec by default. Pass validate=False only
when you intentionally want to inspect or repair an in-progress invalid design.
These helpers are useful in scripts and automated checks.
from tensor_network_editor import (
analyze_contraction,
analyze_spec,
canonicalize_spec,
diff_specs,
lint_spec,
load_spec,
semantic_diff_specs,
validate_spec,
)
spec = load_spec("my_network.json")
validation_issues = validate_spec(spec)
lint_report = lint_spec(spec, max_tensor_rank=8, max_tensor_cardinality=50_000)
analysis = analyze_spec(spec, memory_dtype="float32")
contraction = analyze_contraction(spec, memory_dtype="float32")
canonical_spec = canonicalize_spec(spec, deterministic_ids=False)
diff = diff_specs(spec, spec)
semantic_diff = semantic_diff_specs(spec, canonical_spec)
print(validation_issues)
print(lint_report.to_dict())
print(analysis.to_dict())
print(contraction.to_dict())
print(diff.to_dict())
print(semantic_diff.to_dict())Use:
validate_spec(...)for hard consistency ruleslint_spec(...)for softer warnings and suggestions, including metadata-aware checks built on guided keys likerole,symmetry,leg_kind, andobservableanalyze_spec(...)for structural counts and contraction summariesanalyze_contraction(...)when you only need contraction analysis- normal-mode hyperedges are analyzed as internal generated copy tensors; the
returned contraction result includes
warningsandsynthetic_operandsso callers can explain that lowering to users canonicalize_spec(...)for stable ordering, recursive metadata key ordering, normalizedmetadata.tags, and optional deterministic idsdiff_specs(...)to compare entities by stable idssemantic_diff_specs(...)to report field-level tensor/index/edge/plan/step changes after the same normalization used by canonicalization
Supported memory dtypes for analysis are float16, float32, float64,
complex64, and complex128.
List built-in templates:
from tensor_network_editor import list_template_names
print(list_template_names())Build a template spec:
from tensor_network_editor.templates import build_template_spec, parse_template_parameters
parameters = parse_template_parameters(
"mps",
{
"graph_size": 6,
"bond_dimension": 4,
"physical_dimension": 2,
},
)
spec = build_template_spec(
"mps",
parameters=parameters,
)The advanced template helpers live under tensor_network_editor.templates.
The package root only re-exports build_template_spec(...) and
list_template_names().
Templates are useful when you want a valid starting network without placing
every tensor manually.
Built-ins include mps, mpo, peps_2x2, mera, ttn, pepo, and
tebd_gate_layer. The mps template can also parse
boundary_condition, symmetry, and initial_state options. The mpo
template can also parse boundary_condition, j, and h, and the ttn
template can also parse depth, leaf_physical_legs, root_open_leg, and
isometric when you pass them through parse_template_parameters(...).
Reusable subnetworks are normal NetworkSpec fragments that contain selected
tensors and the connections fully inside that selection.
from tensor_network_editor import CanvasPosition, load_spec
from tensor_network_editor.subnetworks import (
extract_subnetwork_spec,
prepare_subnetwork_for_insertion,
)
source = load_spec("my_network.json")
fragment = extract_subnetwork_spec(
source,
tensor_ids=["tensor_a", "tensor_b"],
name="local_pair",
)
prepared = prepare_subnetwork_for_insertion(
fragment,
target_center=CanvasPosition(x=400.0, y=260.0),
)Important behavior:
- extraction works only in normal graph mode
- extracted fragments keep tensors, groups, pairwise edges, and hyperedges that are fully contained inside the selected tensor set
- notes and contraction plans are intentionally not copied into fragments
- insertion preparation regenerates network, tensor, index, edge, hyperedge,
and group ids, then recenters the tensors around
target_center
For persistent project/shared catalogs, use the editor library dialog or the
tensor-network-editor subnetwork ... CLI commands.
Advanced registration hooks now live in explicit public modules when you want to extend the editor instead of only consuming it.
Inspect registered generator names:
from tensor_network_editor.codegen.registry import list_generator_names
print(list_generator_names())Register a fixed project template from an existing NetworkSpec:
from tensor_network_editor.models import NetworkSpec
from tensor_network_editor.templates import register_static_template
spec = NetworkSpec(name="Reference cell")
register_static_template(
"reference_cell",
"Reference Cell",
spec,
overwrite=True,
)Register a parameterized template builder:
from tensor_network_editor.models import NetworkSpec
from tensor_network_editor.templates import (
TemplateDefinition,
TemplateParameters,
register_template,
)
def build_demo_template(parameters: TemplateParameters) -> NetworkSpec:
return NetworkSpec(
name=f"Demo ({parameters.graph_size})",
)
register_template(
"demo_template",
TemplateDefinition(
name="demo_template",
display_name="Demo Template",
graph_size_label="Sites",
defaults=TemplateParameters(
graph_size=4,
bond_dimension=2,
physical_dimension=2,
),
),
build_demo_template,
overwrite=True,
)Register a custom backend code generator:
from tensor_network_editor.codegen.registry import register_generator
from tensor_network_editor.codegen.shared.base import CodeGenerator
from tensor_network_editor.models import (
CodegenResult,
NetworkSpec,
TensorCollectionFormat,
)
class MyGenerator(CodeGenerator):
@property
def engine(self) -> str:
return "my_backend"
def generate(
self,
spec: NetworkSpec,
collection_format: TensorCollectionFormat = TensorCollectionFormat.LIST,
*,
validate: bool = True,
) -> CodegenResult:
del spec, collection_format, validate
return CodegenResult(engine="my_backend", code="# custom backend")
register_generator("my_backend", MyGenerator(), overwrite=True)Registration names must use lowercase letters, digits, and underscores. Use
overwrite=True only when you intentionally want to replace an existing entry.
Common public errors include:
CodeGenerationError: the requested backend cannot represent the export- serialization and validation errors from loading malformed designs
- normal
ValueErrorfor unsupported option values
For backend-specific fixes, see troubleshooting.md.
This example builds a small network by hand and generates NumPy einsum code.
from tensor_network_editor import (
CanvasPosition,
EdgeEndpointRef,
EdgeSpec,
EngineName,
IndexSpec,
NetworkSpec,
TensorSpec,
generate_code,
save_spec,
)
def main() -> None:
spec = NetworkSpec(
id="network_demo",
name="demo",
tensors=[
TensorSpec(
id="tensor_a",
name="A",
position=CanvasPosition(x=120.0, y=160.0),
indices=[
IndexSpec(id="tensor_a_i", name="i", dimension=2),
IndexSpec(id="tensor_a_x", name="x", dimension=3),
],
),
TensorSpec(
id="tensor_b",
name="B",
position=CanvasPosition(x=360.0, y=160.0),
indices=[
IndexSpec(id="tensor_b_x", name="x", dimension=3),
IndexSpec(id="tensor_b_j", name="j", dimension=4),
],
),
],
edges=[
EdgeSpec(
id="edge_x",
name="bond_x",
left=EdgeEndpointRef(
tensor_id="tensor_a",
index_id="tensor_a_x",
),
right=EdgeEndpointRef(
tensor_id="tensor_b",
index_id="tensor_b_x",
),
)
],
)
save_spec(spec, path="demo_network.json")
result = generate_code(spec, engine=EngineName.EINSUM_NUMPY)
print(result.code)
if __name__ == "__main__":
main()
