Broadcasting & alignment
When both operands of a pointwise op are fully-named XTensors, their axes
are aligned by name — and, where a shared dimension is labelled on both
sides, by label — the xarray way.
Broadcast by name
A pointwise op (+, -, *, /, a comparison, …) between two fully-named
XTensors pairs their axes up by name rather than by position:
a = xtensor(torch.arange(6).reshape(2, 3), names=("x", "y"))
b = xtensor(torch.arange(6).reshape(3, 2), names=("y", "x")) # transposed
(a + b).names # ('x', 'y') — b is transposed to match, then added
c = xtensor(torch.arange(2), names=("x",))
d = xtensor(torch.arange(3), names=("y",))
(c + d).shape # (2, 3) — disjoint dims broadcast to the outer grid
The result's dimensions are the union of the operands' names; a shared name is broadcast together (its sizes must match, or one must be 1) and coordinates that agree are carried through.
Partially-named operands
An XTensor may have unnamed (None) axes — a plain torch tensor usually
has an anonymous batch axis. Name-alignment still applies as long as the
unnamed axes are all leading (the common "a few batch dims, then named
axes" layout): the named suffix aligns by name (union, transpose-to-match,
broadcast a missing axis) while the leading anonymous run broadcasts
positionally, right-aligned like ordinary torch batch dims.
a = xtensor(torch.zeros(3, 3), names=("x", "y"))
b = xtensor(torch.zeros(4, 3, 3), names=(None, "x", "y")) # a batch of them
(a + b).names # (None, 'x', 'y') — 'x'/'y' aligned, batch broadcast
Two more cases:
- Identical names align 1:1 by position, so a non-leading
Noneis fine when both operands share the exact samenames(x(a, None) + y(a, None)). - An operand that is all-unnamed (every axis
None), a plain tensor, or a scalar has nothing to align on and broadcasts positionally — the plain-torch rule (this matches xarray, which broadcasts a bare array against the trailing axes).
What is not allowed is a None sitting after a named axis on operands
whose names differ — e.g. x(a, None) + y(b, None). There, aligning by name is
ambiguous and silent positional broadcasting could pair the wrong axes, so the
op raises. Name every axis (refine_names) or move the unnamed axes to the
front. (This is the resolution of
#75.)
Coordinate alignment
When a shared dimension is labelled on both operands but the labels are in a
different order — or only partly overlap — the operands are aligned by label
before the op, the xarray join="inner" way: both are reindexed to the
intersection of their labels (in the left operand's order), so positions are
matched by label, not by position.
a = xtensor(torch.tensor([1., 2., 3.]), names=("x",), coords={"x": ("A", "B", "C")})
b = xtensor(torch.tensor([10., 20., 30.]), names=("x",), coords={"x": ("C", "B", "A")})
(a + b).coords # {'x': ('A', 'B', 'C')}
(a + b).tolist() # [31.0, 22.0, 13.0] — A+A, B+B, C+C
c = xtensor(torch.tensor([1., 2., 3.]), names=("x",), coords={"x": ("A", "B", "C")})
d = xtensor(torch.tensor([10., 20., 30.]), names=("x",), coords={"x": ("B", "C", "D")})
(c + d).coords # {'x': ('B', 'C')} — inner join to the overlap
A dimension labelled on only one side has nothing to align against, so its labels simply ride along and the op stays positional.