Files
SuperTensor/references/api.md
T
dela 7b59c81d02 Add flow layout: cursor placement, left rail, declared band heights
Figures were positioned by hand-written offsets. Every gap was a magic
number tuned against the content that happened to be there, so a label
that grew two characters landed on the next tensor, and two stages
started from two different x shared no rail. Both failures compile
cleanly.

Replace it with a cursor. Objects placed with an empty coordinate
argument reserve their own width -- including a stack's offset sheets
and a bracket's overhang -- and gaps are declared once (\stgutter,
\strowgap, \stblockgap). The gap belongs to the object that follows it
and the first object in a band gets none, so every band starts flush on
a shared rail and gap=0pt states that two shards tile exactly.

\stlink makes a connector's label a flow object, which is what removes
the label-wider-than-its-arrow failure entirely. \stcol is a vertical
sub-flow for a split along the contracted axis. \strow declares its
height, so an object that does not fit -- or a column that does not add
up to what it declared, and is therefore drawn off-center -- becomes a
package warning, which build.sh fails on.

Absolute placement is unchanged: passing a coordinate takes the original
code path, and \sttrack folds a hand-placed node back into the cursor.

All three golden examples and the new tests/flow.tex are converted and
build clean.
2026-08-05 15:55:21 +08:00

191 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# supertensor.sty API
```tex
\documentclass[border=10pt]{standalone}
\usepackage[cjk]{supertensor} % cjk: ctex + fandol (XeLaTeX). en: English rail labels.
```
Build with `./scripts/build.sh fig.tex` — it puts `assets/` on `TEXINPUTS`, so the package
does not need to be installed into your texmf tree.
## Ledgers
```tex
\stsetrole{q}{stTeal} % role -> color. Macros take a ROLE, never a color.
\stdim{T}{6} % symbolic axis -> physical edge length in cells
\stsetauthor{...} % default 五道口纳什
\stsetlabels{A}{O}{M} % override the three meaning-box rail labels
\stsetrail{3.2em} % width of the bold label rail
```
Colors: `stTeal stOrange stCoral stViolet stGray stInk`. An unknown role falls back to gray
**and emits a package warning**, which `build.sh` turns into a failed build.
Lengths: `\stunit` (one cell, 4.6 mm) and `\sttilegap` (white gutter, 0.5 mm).
## Flow layout
**This is the default. Leave the coordinate argument empty and the object is placed by a
cursor.** Hand-written offsets are the main source of layout bugs in these figures: every
gap becomes a tuned magic number, so a label that grows by two characters silently lands on
the next tensor, and two stages started from two different `x` share no rail.
```tex
\ststage{SA}{stage heading} % on the left rail, below the previous band
\strow{rowA}{T} % open a band, declared height T
\stface[role=q]{Q}{}{T}{d} % empty coord = place at the cursor
\stglyph{mA}{$\times$} % operator glyph; reserves its own width
\stface[role=w]{W}{}{d}{d}
\stlink{lA}{softmax} % connector whose LABEL is a flow object
\stface[role=s]{S}{}{T}{d}
\strowend % fit the band, arm the caption lanes
\stcaption{Q}{$\mathbf Q$}{$T\times d$}
```
| macro | does |
|---|---|
| `\ststage{name}{text}` | stage heading on the left rail, below all ink so far |
| `\strow{name}{height}` | open a band; `height` is an axis name or an integer |
| `\strowend` | `fit` the band into `name`, then `\stlane` it |
| `\stcol{name}{height}` … `\stcolend` | vertical sub-flow filling one slot of the band |
| `\stglyph{name}{$\times$}` | operator glyph (not `\stop` — plain TeX owns that name) |
| `\stcomm{name}{All-Reduce}` | collective node |
| `\stnode[style]{name}{text}` | any node, placed and measured by the cursor |
| `\stlink{name}{label}` | connector; empty label reserves `\stlinklen` of bare arrow |
| `\stgap{4mm}` / `\stvgap{4mm}` | one-off extra space, horizontal / vertical |
| `\stbbox{all}` | everything drawn so far, as one node, for `\stmeaningbox` |
| `\sttopformula{F}{math}` | the formula line, centered on what was actually drawn |
| `\sttrack{node}` | fold a hand-placed node into the bbox and the vertical cursor |
| `\stleftrail{x}` / `\stlayoutreset` | move the rail / start over |
Gaps are declared once: `\stgutter` (6 mm, between objects in a band), `\strowgap` (3.5 mm,
above a band), `\stblockgap` (9 mm, above a stage heading), `\stlinklen` (10 mm).
Four properties follow by construction, and each of them is a bug class removed:
- **The gap belongs to the object that *follows* it**, and the first object in a band gets
none — so every band starts flush on the same rail, and `gap=0pt` means *exactly
adjacent*, which is how shards are made to tile their parent.
- **Every object reserves its own width**, including a stack's offset sheets and a bracket's
overhang. `right=6mm of X` reserves nothing, so the next face is free to land on top.
- **A connector's label is a flow object**, so a label can never be wider than its arrow.
- **A band declares its height**, so an object that does not fit is a *build failure*
(`Package supertensor Warning`), not something the reader discovers.
Call `\sttopformula` **after** the bands. A formula placed first can only be centered on a
figure whose width is not yet known — that is how the top line ends up visibly off-center.
`\stcol` is what a split along the contracted axis looks like:
```tex
\stcol{W2}{dff} % declared total height
\stface[role=r1]{W2a}{}{dffl}{d}
\stface[role=r2, gap=0pt]{W2b}{}{dffl}{d} % tiles W2a exactly
\stcolend
```
A column that consumes a height other than the one it declares is drawn off-center, so that
is a warning too.
Absolute placement still works everywhere — pass a coordinate instead of `{}`. Mix freely,
but wrap hand-placed nodes in `\sttrack` so the cursor knows about them.
## Faces
```tex
\stface[keys]{name}{}{rows}{cols} % flow
\stface[keys]{name}{(coord)}{rows}{cols} % absolute
\ststack[keys]{name}{(coord)}{rows}{cols}{sheets}
\stindexface[keys]{name}{(coord)}{rows}{cols}{entries}
```
`rows`/`cols` accept a declared axis name or a raw integer. A non-empty `(coord)` must
include its own parentheses — `{(0,0)}`, `{($(A.east)+(1.5,0)$)}`. `name` becomes a TikZ
node you can anchor against; `\ststack` also defines `name-front`.
Keys:
| key | default | meaning |
|---|---|---|
| `role=` | `neutral` | hue, via `\stsetrole` |
| `pattern=` | `dense` | `solid dense diag band lower upper causal empty data` |
| `data=` | — | with `pattern=data`: comma-separated rows, one digit per cell, `0`–`3` = level |
| `level=` | `2` | level for `pattern=solid` and the flat level of a mask |
| `bracket=` | `false` | thin neutral matrix brackets |
| `border=` | `true` | outer `black!60` border |
| `tiles=` | `true` | `false` = one flat filled rectangle |
| `gap=` | `\stgutter` | flow only: space *before* this object. `gap=0pt` = exactly adjacent |
`\stindexface` entries are row-major, `rows*cols` of them; `.` leaves a cell blank.
It deliberately has no lightness ramp — see `semantics.md`.
```tex
\stface[role=w, pattern=data, level=3,
data={3300,0330,0033,3003,3030,0303}]{D}{(0,0)}{T}{E}
\stindexface[role=idx]{I}{(b)}{T}{k}{0,1, 1,2, 2,3, 3,0, 0,2, 1,3}
```
## Captions
`\strowend` calls `\stlane` for you, so in flow mode captions just follow the band:
```tex
\strowend
\stcaption{A}{$\mathbf A$}{$T\times d$} % symbol lane, shape lane
\stcaptiontop{A}{\stshapefont{token}} % occasional label above a face
```
Arming the lane by hand (absolute placement):
```tex
\node[inner sep=0pt, fit=(A)(B)(C)] (rowA) {};
\stlane{rowA}
\stcaption{A}{$\mathbf A$}{$T\times d$}
\stnolane
```
`\stcaption` defines `name-sym` and `name-shape` nodes; anchor the next stage heading
against `name-shape.south`.
## Operators, connectors, nodes
Prefer `\stglyph` / `\stcomm` / `\stlink` (above). The raw forms are for absolute placement:
```tex
\node[st op, right=6mm of A] (m) {$\times$}; % reserves nothing -- see layout.md
\node[st comm, right=9mm of P] (ar) {All-Reduce};
\starrow{P.east}{ar.west}
\starrowlabel{M.east}{A.west}{softmax}
```
Styles: `st sym st shape st stage st op st note st arrow st comm st brace`.
Text helpers: `\stformula \ststagelabel \stoperator \stsymfont \stshapefont \stprose`.
Connectors route on the background layer automatically.
## Bottom
```tex
\stbbox{all} % flow: everything drawn so far
\stmeaningbox{mb}{16.6cm}{all}{axes text}{objects text}{mechanism text}
\stsignature{因果多头注意力(掩码 + 拼接投影)}{mb}
```
Arg 2 is the total box width; arg 3 is the node it hangs below. `\stbbox` already contains
every caption and top label; if you build the `fit` by hand, include them yourself or the
box will overlap them. An empty `{}` row is dropped.
## Gotchas
- `\ststack` never re-enters `\stface`; if you extend the package, do not pass
`role=\st@role` back through pgfkeys — it defines the macro in terms of itself and hangs.
- A coordinate expression inside `fit=` needs braces: `fit={(a) ($(b)+(1,0)$)}`.
- `\foreach {\macro,...,1}` cannot infer its direction from an unexpanded macro.
- `\strole` is expandable on purpose (it is used inside `\edef`); the warning lives in
`\stcheckrole`.
- Neither the TikZ path parser nor the `calc` library expands a macro sitting where it
expects `(`. Every cursor-computed coordinate therefore reaches the parser as literal
text, via `\edef ... \noexpand`. Same class of trap: pgfmath cannot digest `\stresolve`'s
`\ifcsname`, so a ledger lookup must be pre-resolved with `\edef` before it is measured.
- `\sttopformula` puts a group around its argument, which breaks TikZ's own `\\`. For more
than one line, wrap the math in amsmath's `gathered`.