tensor-network-visualization 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. tensor_network_visualization-1.0.0/LICENSE +21 -0
  2. tensor_network_visualization-1.0.0/PKG-INFO +104 -0
  3. tensor_network_visualization-1.0.0/README.md +80 -0
  4. tensor_network_visualization-1.0.0/pyproject.toml +50 -0
  5. tensor_network_visualization-1.0.0/setup.cfg +4 -0
  6. tensor_network_visualization-1.0.0/src/tensor_network_visualization.egg-info/PKG-INFO +104 -0
  7. tensor_network_visualization-1.0.0/src/tensor_network_visualization.egg-info/SOURCES.txt +22 -0
  8. tensor_network_visualization-1.0.0/src/tensor_network_visualization.egg-info/dependency_links.txt +1 -0
  9. tensor_network_visualization-1.0.0/src/tensor_network_visualization.egg-info/requires.txt +8 -0
  10. tensor_network_visualization-1.0.0/src/tensor_network_visualization.egg-info/top_level.txt +1 -0
  11. tensor_network_visualization-1.0.0/src/tensor_network_viz/__init__.py +10 -0
  12. tensor_network_visualization-1.0.0/src/tensor_network_viz/config.py +60 -0
  13. tensor_network_visualization-1.0.0/src/tensor_network_viz/py.typed +0 -0
  14. tensor_network_visualization-1.0.0/src/tensor_network_viz/tensorkrowch/__init__.py +9 -0
  15. tensor_network_visualization-1.0.0/src/tensor_network_viz/tensorkrowch/_draw_common.py +62 -0
  16. tensor_network_visualization-1.0.0/src/tensor_network_viz/tensorkrowch/curves.py +98 -0
  17. tensor_network_visualization-1.0.0/src/tensor_network_viz/tensorkrowch/draw_2d.py +178 -0
  18. tensor_network_visualization-1.0.0/src/tensor_network_viz/tensorkrowch/draw_3d.py +202 -0
  19. tensor_network_visualization-1.0.0/src/tensor_network_viz/tensorkrowch/graph.py +190 -0
  20. tensor_network_visualization-1.0.0/src/tensor_network_viz/tensorkrowch/layout.py +352 -0
  21. tensor_network_visualization-1.0.0/src/tensor_network_viz/tensorkrowch/renderer.py +181 -0
  22. tensor_network_visualization-1.0.0/src/tensor_network_viz/viewer.py +57 -0
  23. tensor_network_visualization-1.0.0/tests/test_integration_tensorkrowch.py +26 -0
  24. tensor_network_visualization-1.0.0/tests/test_plotting.py +156 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alejandro Mata Ali
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,104 @@
1
+ Metadata-Version: 2.4
2
+ Name: tensor-network-visualization
3
+ Version: 1.0.0
4
+ Summary: Minimal Matplotlib visualizations for TensorKrowch tensor networks.
5
+ Author: Alejandro Mata Ali
6
+ License: MIT
7
+ Project-URL: Repository, https://github.com/DOKOS-TAYOS/Tensor-Network-Visualization
8
+ Keywords: tensor-network,visualization,tensorkrowch,matplotlib
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3 :: Only
12
+ Classifier: Topic :: Scientific/Engineering :: Visualization
13
+ Requires-Python: >=3.10
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: matplotlib>=3.7
17
+ Requires-Dist: networkx>=3.0
18
+ Requires-Dist: tensorkrowch
19
+ Provides-Extra: dev
20
+ Requires-Dist: pytest; extra == "dev"
21
+ Requires-Dist: ruff; extra == "dev"
22
+ Requires-Dist: pyright; extra == "dev"
23
+ Dynamic: license-file
24
+
25
+ # Tensor-Network-Visualization
26
+
27
+ Minimal Matplotlib visualizations for TensorKrowch tensor networks.
28
+
29
+ **Repository:** [https://github.com/DOKOS-TAYOS/Tensor-Network-Visualization](https://github.com/DOKOS-TAYOS/Tensor-Network-Visualization)
30
+
31
+ ## Features
32
+
33
+ - 2D and 3D plotting for TensorKrowch tensor networks
34
+ - Tensors rendered as nodes
35
+ - Contractions rendered as edges between tensors
36
+ - Dangling indices rendered as labeled stubs
37
+ - Self-contractions rendered as loops
38
+ - No UI; uses Matplotlib for rendering and NetworkX for graph layouts
39
+
40
+ ## Installation
41
+
42
+ ### As a dependency
43
+
44
+ Add to your project's `pyproject.toml`:
45
+
46
+ ```toml
47
+ [project]
48
+ dependencies = ["tensor-network-visualization>=0.1.0"]
49
+ ```
50
+
51
+ Or install with pip:
52
+
53
+ ```bash
54
+ pip install tensor-network-visualization
55
+ ```
56
+
57
+ ### Local development
58
+
59
+ Inside the project virtual environment:
60
+
61
+ ```powershell
62
+ .\.venv\Scripts\python -m pip install -e ".[dev]"
63
+ ```
64
+
65
+ For runtime-only (editable install without dev tools):
66
+
67
+ ```powershell
68
+ .\.venv\Scripts\python -m pip install -e .
69
+ ```
70
+
71
+ ## Usage
72
+
73
+ Networks must expose `nodes` or `leaf_nodes` (iterable or dict). Each node must have `edges`, `axes_names`, and `name`. Each edge must have `node1`, `node2`, and `name`.
74
+
75
+ ```python
76
+ from tensor_network_viz import PlotConfig, show_tensor_network
77
+
78
+ config = PlotConfig(figsize=(8, 6))
79
+ fig, ax = show_tensor_network(
80
+ network,
81
+ engine="tensorkrowch",
82
+ view="2d",
83
+ config=config,
84
+ )
85
+ ```
86
+
87
+ You can also use the TensorKrowch-specific helpers directly:
88
+
89
+ ```python
90
+ from tensor_network_viz.tensorkrowch import plot_tensorkrowch_network_2d, plot_tensorkrowch_network_3d
91
+ ```
92
+
93
+ ## Project layout
94
+
95
+ - `examples/` — Demo scripts. Run `python examples/tensor_network_demo.py mps 2d` from the project root.
96
+ - `scripts/` — Utility scripts (e.g. `clean.py` to remove caches and build artifacts).
97
+
98
+ ## Development
99
+
100
+ ```powershell
101
+ .\.venv\Scripts\python -m ruff check .
102
+ .\.venv\Scripts\python -m pyright
103
+ .\.venv\Scripts\python -m pytest
104
+ ```
@@ -0,0 +1,80 @@
1
+ # Tensor-Network-Visualization
2
+
3
+ Minimal Matplotlib visualizations for TensorKrowch tensor networks.
4
+
5
+ **Repository:** [https://github.com/DOKOS-TAYOS/Tensor-Network-Visualization](https://github.com/DOKOS-TAYOS/Tensor-Network-Visualization)
6
+
7
+ ## Features
8
+
9
+ - 2D and 3D plotting for TensorKrowch tensor networks
10
+ - Tensors rendered as nodes
11
+ - Contractions rendered as edges between tensors
12
+ - Dangling indices rendered as labeled stubs
13
+ - Self-contractions rendered as loops
14
+ - No UI; uses Matplotlib for rendering and NetworkX for graph layouts
15
+
16
+ ## Installation
17
+
18
+ ### As a dependency
19
+
20
+ Add to your project's `pyproject.toml`:
21
+
22
+ ```toml
23
+ [project]
24
+ dependencies = ["tensor-network-visualization>=0.1.0"]
25
+ ```
26
+
27
+ Or install with pip:
28
+
29
+ ```bash
30
+ pip install tensor-network-visualization
31
+ ```
32
+
33
+ ### Local development
34
+
35
+ Inside the project virtual environment:
36
+
37
+ ```powershell
38
+ .\.venv\Scripts\python -m pip install -e ".[dev]"
39
+ ```
40
+
41
+ For runtime-only (editable install without dev tools):
42
+
43
+ ```powershell
44
+ .\.venv\Scripts\python -m pip install -e .
45
+ ```
46
+
47
+ ## Usage
48
+
49
+ Networks must expose `nodes` or `leaf_nodes` (iterable or dict). Each node must have `edges`, `axes_names`, and `name`. Each edge must have `node1`, `node2`, and `name`.
50
+
51
+ ```python
52
+ from tensor_network_viz import PlotConfig, show_tensor_network
53
+
54
+ config = PlotConfig(figsize=(8, 6))
55
+ fig, ax = show_tensor_network(
56
+ network,
57
+ engine="tensorkrowch",
58
+ view="2d",
59
+ config=config,
60
+ )
61
+ ```
62
+
63
+ You can also use the TensorKrowch-specific helpers directly:
64
+
65
+ ```python
66
+ from tensor_network_viz.tensorkrowch import plot_tensorkrowch_network_2d, plot_tensorkrowch_network_3d
67
+ ```
68
+
69
+ ## Project layout
70
+
71
+ - `examples/` — Demo scripts. Run `python examples/tensor_network_demo.py mps 2d` from the project root.
72
+ - `scripts/` — Utility scripts (e.g. `clean.py` to remove caches and build artifacts).
73
+
74
+ ## Development
75
+
76
+ ```powershell
77
+ .\.venv\Scripts\python -m ruff check .
78
+ .\.venv\Scripts\python -m pyright
79
+ .\.venv\Scripts\python -m pytest
80
+ ```
@@ -0,0 +1,50 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "tensor-network-visualization"
7
+ version = "1.0.0"
8
+ description = "Minimal Matplotlib visualizations for TensorKrowch tensor networks."
9
+ authors = [{ name = "Alejandro Mata Ali" }]
10
+ readme = "README.md"
11
+ license = { text = "MIT" }
12
+ requires-python = ">=3.10"
13
+ keywords = ["tensor-network", "visualization", "tensorkrowch", "matplotlib"]
14
+ dependencies = [
15
+ "matplotlib>=3.7",
16
+ "networkx>=3.0",
17
+ "tensorkrowch",
18
+ ]
19
+ classifiers = [
20
+ "License :: OSI Approved :: MIT License",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3 :: Only",
23
+ "Topic :: Scientific/Engineering :: Visualization",
24
+ ]
25
+
26
+ [project.urls]
27
+ Repository = "https://github.com/DOKOS-TAYOS/Tensor-Network-Visualization"
28
+
29
+ [project.optional-dependencies]
30
+ dev = ["pytest", "ruff", "pyright"]
31
+
32
+ [tool.setuptools.packages.find]
33
+ where = ["src"]
34
+
35
+ [tool.setuptools.package-data]
36
+ tensor_network_viz = ["py.typed"]
37
+
38
+ [tool.pytest.ini_options]
39
+ testpaths = ["tests"]
40
+ addopts = "-p no:cacheprovider"
41
+ pythonpath = ["src"]
42
+
43
+ [tool.ruff]
44
+ line-length = 100
45
+ target-version = "py310"
46
+ src = ["src", "tests"]
47
+ exclude = [".venv", ".tmp", ".pip_tmp", ".pytest_cache", ".ruff_cache", "pytest-cache-files-*", "tn_tsp.py"]
48
+
49
+ [tool.ruff.lint]
50
+ select = ["E", "F", "I", "B", "UP", "C4", "SIM"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,104 @@
1
+ Metadata-Version: 2.4
2
+ Name: tensor-network-visualization
3
+ Version: 1.0.0
4
+ Summary: Minimal Matplotlib visualizations for TensorKrowch tensor networks.
5
+ Author: Alejandro Mata Ali
6
+ License: MIT
7
+ Project-URL: Repository, https://github.com/DOKOS-TAYOS/Tensor-Network-Visualization
8
+ Keywords: tensor-network,visualization,tensorkrowch,matplotlib
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3 :: Only
12
+ Classifier: Topic :: Scientific/Engineering :: Visualization
13
+ Requires-Python: >=3.10
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: matplotlib>=3.7
17
+ Requires-Dist: networkx>=3.0
18
+ Requires-Dist: tensorkrowch
19
+ Provides-Extra: dev
20
+ Requires-Dist: pytest; extra == "dev"
21
+ Requires-Dist: ruff; extra == "dev"
22
+ Requires-Dist: pyright; extra == "dev"
23
+ Dynamic: license-file
24
+
25
+ # Tensor-Network-Visualization
26
+
27
+ Minimal Matplotlib visualizations for TensorKrowch tensor networks.
28
+
29
+ **Repository:** [https://github.com/DOKOS-TAYOS/Tensor-Network-Visualization](https://github.com/DOKOS-TAYOS/Tensor-Network-Visualization)
30
+
31
+ ## Features
32
+
33
+ - 2D and 3D plotting for TensorKrowch tensor networks
34
+ - Tensors rendered as nodes
35
+ - Contractions rendered as edges between tensors
36
+ - Dangling indices rendered as labeled stubs
37
+ - Self-contractions rendered as loops
38
+ - No UI; uses Matplotlib for rendering and NetworkX for graph layouts
39
+
40
+ ## Installation
41
+
42
+ ### As a dependency
43
+
44
+ Add to your project's `pyproject.toml`:
45
+
46
+ ```toml
47
+ [project]
48
+ dependencies = ["tensor-network-visualization>=0.1.0"]
49
+ ```
50
+
51
+ Or install with pip:
52
+
53
+ ```bash
54
+ pip install tensor-network-visualization
55
+ ```
56
+
57
+ ### Local development
58
+
59
+ Inside the project virtual environment:
60
+
61
+ ```powershell
62
+ .\.venv\Scripts\python -m pip install -e ".[dev]"
63
+ ```
64
+
65
+ For runtime-only (editable install without dev tools):
66
+
67
+ ```powershell
68
+ .\.venv\Scripts\python -m pip install -e .
69
+ ```
70
+
71
+ ## Usage
72
+
73
+ Networks must expose `nodes` or `leaf_nodes` (iterable or dict). Each node must have `edges`, `axes_names`, and `name`. Each edge must have `node1`, `node2`, and `name`.
74
+
75
+ ```python
76
+ from tensor_network_viz import PlotConfig, show_tensor_network
77
+
78
+ config = PlotConfig(figsize=(8, 6))
79
+ fig, ax = show_tensor_network(
80
+ network,
81
+ engine="tensorkrowch",
82
+ view="2d",
83
+ config=config,
84
+ )
85
+ ```
86
+
87
+ You can also use the TensorKrowch-specific helpers directly:
88
+
89
+ ```python
90
+ from tensor_network_viz.tensorkrowch import plot_tensorkrowch_network_2d, plot_tensorkrowch_network_3d
91
+ ```
92
+
93
+ ## Project layout
94
+
95
+ - `examples/` — Demo scripts. Run `python examples/tensor_network_demo.py mps 2d` from the project root.
96
+ - `scripts/` — Utility scripts (e.g. `clean.py` to remove caches and build artifacts).
97
+
98
+ ## Development
99
+
100
+ ```powershell
101
+ .\.venv\Scripts\python -m ruff check .
102
+ .\.venv\Scripts\python -m pyright
103
+ .\.venv\Scripts\python -m pytest
104
+ ```
@@ -0,0 +1,22 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/tensor_network_visualization.egg-info/PKG-INFO
5
+ src/tensor_network_visualization.egg-info/SOURCES.txt
6
+ src/tensor_network_visualization.egg-info/dependency_links.txt
7
+ src/tensor_network_visualization.egg-info/requires.txt
8
+ src/tensor_network_visualization.egg-info/top_level.txt
9
+ src/tensor_network_viz/__init__.py
10
+ src/tensor_network_viz/config.py
11
+ src/tensor_network_viz/py.typed
12
+ src/tensor_network_viz/viewer.py
13
+ src/tensor_network_viz/tensorkrowch/__init__.py
14
+ src/tensor_network_viz/tensorkrowch/_draw_common.py
15
+ src/tensor_network_viz/tensorkrowch/curves.py
16
+ src/tensor_network_viz/tensorkrowch/draw_2d.py
17
+ src/tensor_network_viz/tensorkrowch/draw_3d.py
18
+ src/tensor_network_viz/tensorkrowch/graph.py
19
+ src/tensor_network_viz/tensorkrowch/layout.py
20
+ src/tensor_network_viz/tensorkrowch/renderer.py
21
+ tests/test_integration_tensorkrowch.py
22
+ tests/test_plotting.py
@@ -0,0 +1,8 @@
1
+ matplotlib>=3.7
2
+ networkx>=3.0
3
+ tensorkrowch
4
+
5
+ [dev]
6
+ pytest
7
+ ruff
8
+ pyright
@@ -0,0 +1,10 @@
1
+ from .config import EngineName, PlotConfig, ViewName
2
+
3
+
4
+ def show_tensor_network(*args, **kwargs):
5
+ from .viewer import show_tensor_network as _show_tensor_network
6
+
7
+ return _show_tensor_network(*args, **kwargs)
8
+
9
+
10
+ __all__ = ["EngineName", "PlotConfig", "ViewName", "show_tensor_network"]
@@ -0,0 +1,60 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Literal, TypeAlias
5
+
6
+ EngineName: TypeAlias = Literal["tensorkrowch"]
7
+ ViewName: TypeAlias = Literal["2d", "3d"]
8
+
9
+
10
+ _DEFAULT_NODE_RADIUS = 0.08
11
+ _DEFAULT_STUB_LENGTH = 0.34
12
+ _DEFAULT_SELF_LOOP_RADIUS = 0.2
13
+ _DEFAULT_LINE_WIDTH_2D = 1.8
14
+ _DEFAULT_LINE_WIDTH_3D = 1.6
15
+
16
+
17
+ @dataclass(frozen=True)
18
+ class PlotConfig:
19
+ """Configuration for tensor network plot styling.
20
+
21
+ Compatible networks must expose `nodes` or `leaf_nodes` (iterable or dict).
22
+ Each node must have `edges`, `axes_names`, and `name`. Each edge must have
23
+ `node1`, `node2`, and `name`.
24
+
25
+ Attributes:
26
+ node_color: Fill color for tensor nodes (hex or named color).
27
+ edge_color: Color for generic edges (currently unused; bond/dangling take precedence).
28
+ label_color: Color for axis and edge labels.
29
+ bond_edge_color: Color for contraction edges between tensors.
30
+ dangling_edge_color: Color for dangling index stubs.
31
+ figsize: Figure size as (width, height) in inches; None uses Matplotlib default.
32
+ show_tensor_labels: Whether to display tensor names on nodes.
33
+ show_index_labels: Whether to display axis names on edges.
34
+ node_radius: Radius of tensor nodes; None uses default (0.08).
35
+ stub_length: Length of dangling index stubs; None uses default (0.34).
36
+ self_loop_radius: Radius for self-contraction loops; None uses default (0.2).
37
+ line_width_2d: Line width for 2D plots; None uses default (1.8).
38
+ line_width_3d: Line width for 3D plots; None uses default (1.6).
39
+ positions: Custom node positions for grid/PEPS layout; dict mapping node id to
40
+ (x, y) for 2D or (x, y, z) for 3D. None uses automatic layout.
41
+ node_edge_color: Border color for tensor nodes; use dark for contrast on light nodes.
42
+ tensor_label_color: Color for tensor names on nodes; use dark for readability.
43
+ """
44
+
45
+ node_color: str = "#E8E8E8"
46
+ node_edge_color: str = "#2D3748"
47
+ tensor_label_color: str = "#1A202C"
48
+ edge_color: str = "#202B33"
49
+ label_color: str = "#0C1319"
50
+ bond_edge_color: str = "#00008B"
51
+ dangling_edge_color: str = "#8B0000"
52
+ figsize: tuple[float, float] | None = (8, 6)
53
+ show_tensor_labels: bool = True
54
+ show_index_labels: bool = True
55
+ node_radius: float | None = None
56
+ stub_length: float | None = None
57
+ self_loop_radius: float | None = None
58
+ line_width_2d: float | None = None
59
+ line_width_3d: float | None = None
60
+ positions: dict[int, tuple[float, ...]] | None = None
@@ -0,0 +1,9 @@
1
+ from .renderer import (
2
+ plot_tensorkrowch_network_2d,
3
+ plot_tensorkrowch_network_3d,
4
+ )
5
+
6
+ __all__ = [
7
+ "plot_tensorkrowch_network_2d",
8
+ "plot_tensorkrowch_network_3d",
9
+ ]
@@ -0,0 +1,62 @@
1
+ """Shared scale and style parameters for 2D and 3D drawing."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+ from ..config import (
8
+ _DEFAULT_LINE_WIDTH_2D,
9
+ _DEFAULT_LINE_WIDTH_3D,
10
+ _DEFAULT_NODE_RADIUS,
11
+ _DEFAULT_SELF_LOOP_RADIUS,
12
+ _DEFAULT_STUB_LENGTH,
13
+ PlotConfig,
14
+ )
15
+
16
+
17
+ @dataclass(frozen=True)
18
+ class _DrawScaleParams:
19
+ """Resolved scale-dependent parameters for drawing."""
20
+
21
+ r: float
22
+ stub: float
23
+ loop_r: float
24
+ lw: float
25
+ font_dangling: int
26
+ font_bond: int
27
+ font_node: int
28
+ label_offset: float
29
+ ellipse_w: float
30
+ ellipse_h: float
31
+ scatter_s: float
32
+
33
+
34
+ def _draw_scale_params(config: PlotConfig, scale: float, *, is_3d: bool) -> _DrawScaleParams:
35
+ """Compute scale-dependent drawing parameters from config."""
36
+ r = (config.node_radius if config.node_radius is not None else _DEFAULT_NODE_RADIUS) * scale
37
+ stub = (
38
+ config.stub_length if config.stub_length is not None else _DEFAULT_STUB_LENGTH
39
+ ) * scale
40
+ loop_r = (
41
+ config.self_loop_radius
42
+ if config.self_loop_radius is not None
43
+ else _DEFAULT_SELF_LOOP_RADIUS
44
+ ) * scale
45
+ lw_default = _DEFAULT_LINE_WIDTH_3D if is_3d else _DEFAULT_LINE_WIDTH_2D
46
+ lw_attr = config.line_width_3d if is_3d else config.line_width_2d
47
+ lw = (lw_attr if lw_attr is not None else lw_default) * scale
48
+ scatter_s = (120 if is_3d else 900) * (scale**2)
49
+
50
+ return _DrawScaleParams(
51
+ r=r,
52
+ stub=stub,
53
+ loop_r=loop_r,
54
+ lw=lw,
55
+ font_dangling=max(7, round(9 * scale)),
56
+ font_bond=max(5, round(5 * scale)),
57
+ font_node=max(8, round(10 * scale)),
58
+ label_offset=0.08 * scale,
59
+ ellipse_w=0.16 * scale,
60
+ ellipse_h=0.12 * scale,
61
+ scatter_s=scatter_s,
62
+ )
@@ -0,0 +1,98 @@
1
+ """Curve geometry and edge grouping utilities."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+ from typing import TypeAlias
7
+
8
+ import numpy as np
9
+
10
+ from .graph import _EdgeData, _EdgeEndpoint, _GraphData
11
+
12
+ Vector: TypeAlias = np.ndarray
13
+
14
+ # For curve offset: positive = curve "above"/"right", negative = "below"/"left"
15
+ _AXIS_OFFSET_SIGN: dict[str, int] = {
16
+ "up": 1,
17
+ "right": 1,
18
+ "north": 1,
19
+ "east": 1,
20
+ "down": -1,
21
+ "left": -1,
22
+ "south": -1,
23
+ "west": -1,
24
+ }
25
+
26
+
27
+ def _offset_sign_from_axis_name(axis_name: str | None) -> int:
28
+ if not axis_name:
29
+ return 0
30
+ return _AXIS_OFFSET_SIGN.get(axis_name.lower().strip(), 0)
31
+
32
+
33
+ def _group_contractions(graph: _GraphData) -> dict[tuple[int, int], list[_EdgeData]]:
34
+ groups: dict[tuple[int, int], list[_EdgeData]] = {}
35
+ for edge in graph.edges:
36
+ if edge.kind != "contraction":
37
+ continue
38
+ key = tuple(sorted(edge.node_ids))
39
+ groups.setdefault(key, []).append(edge)
40
+ for group in groups.values():
41
+ group.sort(key=lambda e: _offset_sign_from_axis_name(
42
+ e.endpoints[0].axis_name if e.endpoints else None
43
+ ))
44
+ return groups
45
+
46
+
47
+ def _quadratic_curve(
48
+ start: Vector,
49
+ control: Vector,
50
+ end: Vector,
51
+ samples: int = 40,
52
+ ) -> Vector:
53
+ t = np.linspace(0.0, 1.0, samples)
54
+ return (
55
+ ((1.0 - t) ** 2)[:, None] * start
56
+ + (2.0 * (1.0 - t) * t)[:, None] * control
57
+ + (t**2)[:, None] * end
58
+ )
59
+
60
+
61
+ def _ellipse_points(
62
+ center: Vector,
63
+ direction: Vector,
64
+ normal: Vector,
65
+ *,
66
+ width: float,
67
+ height: float,
68
+ samples: int = 60,
69
+ ) -> Vector:
70
+ theta = np.linspace(0.0, 2.0 * math.pi, samples)
71
+ return (
72
+ center
73
+ + np.outer(np.cos(theta), direction) * width
74
+ + np.outer(np.sin(theta), normal) * height
75
+ )
76
+
77
+
78
+ def _ellipse_points_3d(
79
+ center: Vector,
80
+ axis_a: Vector,
81
+ axis_b: Vector,
82
+ *,
83
+ width: float,
84
+ height: float,
85
+ samples: int = 60,
86
+ ) -> Vector:
87
+ theta = np.linspace(0.0, 2.0 * math.pi, samples)
88
+ return (
89
+ center
90
+ + np.outer(np.cos(theta), axis_a) * width
91
+ + np.outer(np.sin(theta), axis_b) * height
92
+ )
93
+
94
+
95
+ def _require_self_endpoints(edge: _EdgeData) -> tuple[_EdgeEndpoint, _EdgeEndpoint]:
96
+ if len(edge.endpoints) < 2:
97
+ raise TypeError("Self-edges must expose two endpoints.")
98
+ return edge.endpoints[0], edge.endpoints[1]