|
1 | | -"""Book-grammar layout for tensor diagrams. |
| 1 | +r"""Book-grammar layout for tensor diagrams. |
2 | 2 |
|
3 | 3 | Turns a tensorgrad ``Tensor`` into a diagram laid out the way the Tensor |
4 | 4 | Cookbook draws them by hand. This is deliberately NOT a general graph-drawing |
|
51 | 51 | from numbers import Number |
52 | 52 | from typing import Optional |
53 | 53 |
|
54 | | -from tensorgrad.tensor import Delta, Derivative, Function, Product, Rename, Sum, Tensor, Variable |
| 54 | +from tensorgrad.tensor import ( |
| 55 | + Delta, Derivative, Function, Product, Rename, Sum, Tensor, Variable, Zero, |
| 56 | +) |
55 | 57 |
|
56 | 58 | try: |
57 | 59 | from tensorgrad.extras.expectation import Expectation |
@@ -165,6 +167,15 @@ def walk(t: Tensor) -> dict[str, str]: |
165 | 167 | label = t.name |
166 | 168 | g.atoms.append(AtomSpec(aid, "var", label, toks)) |
167 | 169 | return dict(zip(list(t.edges), toks)) |
| 170 | + if isinstance(t, Zero): |
| 171 | + edges = list(t.edges) |
| 172 | + aid = len(g.atoms) |
| 173 | + if not edges: |
| 174 | + g.atoms.append(AtomSpec(aid, "scalar", "0", [])) |
| 175 | + return {} |
| 176 | + toks = [fresh("w") for _ in edges] |
| 177 | + g.atoms.append(AtomSpec(aid, "var", "0", toks)) |
| 178 | + return dict(zip(edges, toks)) |
168 | 179 | if isinstance(t, Delta): |
169 | 180 | edges = list(t.edges) |
170 | 181 | if len(edges) == 2: |
@@ -1211,11 +1222,28 @@ def to_book_tikz( |
1211 | 1222 | left: Optional[str] = None, |
1212 | 1223 | right: Optional[str] = None, |
1213 | 1224 | baseline: str = "-.25em", |
| 1225 | + scale: Optional[float] = None, |
| 1226 | + max_width: Optional[float] = None, |
1214 | 1227 | ) -> str: |
1215 | | - """Render a tensorgrad Tensor as book-style TikZ (uses tikz-styles.tex).""" |
1216 | | - lines: list[str] = [ |
1217 | | - rf"\begin{{tikzpicture}}[baseline={baseline}, inner sep=1pt]" |
1218 | | - ] |
1219 | | - _emit_layout(layout_any(tensor, left, right), lines, prefix="", dx=0.0) |
| 1228 | + """Render a tensorgrad Tensor as book-style TikZ (uses tikz-styles.tex). |
| 1229 | +
|
| 1230 | + Args: |
| 1231 | + left/right: force the named free edge to exit that side (covariance). |
| 1232 | + baseline: TikZ baseline anchor for inline use. |
| 1233 | + scale: explicit TikZ scale factor for the whole picture. |
| 1234 | + max_width: if the laid-out diagram is wider than this (in cm), scale |
| 1235 | + it down to fit -- wide gradients/products then stay on the page |
| 1236 | + instead of overflowing. Ignored if `scale` is given. |
| 1237 | + """ |
| 1238 | + layout = layout_any(tensor, left, right) |
| 1239 | + if scale is None and max_width is not None and layout.xmax > max_width > 0: |
| 1240 | + scale = max_width / layout.xmax |
| 1241 | + opts = f"baseline={baseline}, inner sep=1pt" |
| 1242 | + if scale is not None and abs(scale - 1.0) > 1e-6: |
| 1243 | + # `transform shape` scales node glyphs too, so the whole diagram |
| 1244 | + # shrinks uniformly instead of nodes overlapping at moved coordinates |
| 1245 | + opts += f", scale={scale:.3f}, transform shape" |
| 1246 | + lines: list[str] = [rf"\begin{{tikzpicture}}[{opts}]"] |
| 1247 | + _emit_layout(layout, lines, prefix="", dx=0.0) |
1220 | 1248 | lines.append(r"\end{tikzpicture}") |
1221 | 1249 | return "\n".join(lines) |
0 commit comments