Part of the v1 convention (#714, spec in arithmetics-design/convention.md, introduced by #717). The rule is specified but not yet implemented — this issue tracks the implementation.
The rule
Unlabeled operands — numpy arrays, lists, polars Series — carry no labels to align by, so they pair with the linopy operand's dimensions by size: each axis adopts the dimension (and the coordinates) whose length matches. The pairing must be determined by the sizes alone — when it would be a guess, raise.
Spec vs. today
With x(time: 5), rect(a: 4, time: 5), sq(a: 4, b: 4):
| Operation |
Today (leading-positional) |
v1 spec (size-determined) |
np.arange(5.) + x |
maps to time |
maps to time (unique match) |
np.arange(4.) + x |
raises (opaque xarray error) |
raises with a linopy message (no size match) |
np.arange(4.) * rect |
maps to a |
maps to a (unique match) |
np.arange(5.) * rect |
raises |
maps to time (unique match, not the leading dim) |
np.arange(4.) * sq |
silently maps to a |
raises — ambiguous (a or b?) |
np.arange(16.).reshape(4,4) * sq |
maps to (a, b) in order |
raises — ambiguous (sizes cannot tell (a, b) from (b, a)) |
[1., 2., 3., 4.] + x |
legacy: silent left-join / v1: coord-mismatch raise |
raises with a linopy message (no size match) |
The fix for an ambiguous case is to name the dimensions: wrap the array in an xarray.DataArray (or use a pandas Series/DataFrame with a named index).
Implementation notes
Part of the v1 convention (#714, spec in
arithmetics-design/convention.md, introduced by #717). The rule is specified but not yet implemented — this issue tracks the implementation.The rule
Unlabeled operands — numpy arrays, lists, polars Series — carry no labels to align by, so they pair with the linopy operand's dimensions by size: each axis adopts the dimension (and the coordinates) whose length matches. The pairing must be determined by the sizes alone — when it would be a guess, raise.
Spec vs. today
With
x(time: 5),rect(a: 4, time: 5),sq(a: 4, b: 4):np.arange(5.) + xtimetime(unique match)np.arange(4.) + xnp.arange(4.) * rectaa(unique match)np.arange(5.) * recttime(unique match, not the leading dim)np.arange(4.) * sqaaorb?)np.arange(16.).reshape(4,4) * sq(a, b)in order(a, b)from(b, a))[1., 2., 3., 4.] + xThe fix for an ambiguous case is to name the dimensions: wrap the array in an
xarray.DataArray(or use a pandas Series/DataFrame with a named index).Implementation notes
as_dataarray(other, coords=self.coords, dims=self.coord_dims)call sites inexpressions.py/variables.py), so it should build on the coords-as-truth /as_dataarrayseam refactored in refactor: unify coords-as-truth handling in add_variables/add_constraints #732 — blocked by refactor: unify coords-as-truth handling in add_variables/add_constraints #732.TestObjectScope) intest/test_legacy_violations.py— the ambiguity raises are genuine legacy violations to pair with@pytest.mark.legacydocumentation tests.