Skip to content

Commit 1da4582

Browse files
committed
docs: add naming and shape conventions guide
1 parent e4f928c commit 1da4582

3 files changed

Lines changed: 224 additions & 0 deletions

File tree

docs/source/contributing.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,10 @@ Example test structure:
195195
Documentation
196196
-------------
197197

198+
See :doc:`naming` for the repository's conventions for sparse layouts, shapes,
199+
batching, dimensions, and specified-entry counts. Follow these conventions in
200+
new code, tests, docstrings, and pull requests.
201+
198202
Documentation is built with Sphinx. To build locally:
199203

200204
.. code-block:: bash

docs/source/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ Sparse Multivariate Normal Distributions
6262
api/index
6363
benchmarks
6464
contributing
65+
naming
6566

6667
Installation
6768
============

docs/source/naming.rst

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
Naming and shape conventions
2+
============================
3+
4+
This guide defines the terminology used for shapes, batching, sparse layouts,
5+
and dimensions in ``torchsparsegradutils``. Use it for code, docstrings,
6+
tests, issues, and pull requests. State the logical object first and then its
7+
shape: sparse tensor terminology otherwise makes it easy to give different
8+
concepts the same name.
9+
10+
Core terminology
11+
----------------
12+
13+
Use **matrix** for the logical object represented by the final two axes, and
14+
use **tensor** for the PyTorch object or for a genuinely non-matrix object.
15+
16+
* An unbatched sparse matrix has shape ``(n_rows, n_cols)``.
17+
* A batched sparse matrix (a batch of sparse matrices) has shape
18+
``(batch_size, n_rows, n_cols)``.
19+
* An unbatched square sparse matrix has shape ``(n, n)``; its batched form has
20+
shape ``(batch_size, n, n)``.
21+
* A dense matrix of right-hand sides has shape ``(n_rows, n_rhs)``; its
22+
batched form has shape ``(batch_size, n_rows, n_rhs)``.
23+
* A vector has shape ``(n,)`` and a batch of vectors has shape
24+
``(batch_size, n)``.
25+
26+
``2D``, ``3D``, ``rank 2``, and ``rank 3`` describe only the number of
27+
tensor axes. They do not identify an axis as a batch, matrix, spatial, or
28+
dense-value axis. Prefer, for example, “a batched sparse matrix with shape
29+
``(batch_size, n_rows, n_cols)``” to “a 3D sparse tensor”.
30+
31+
When an API supports at most one batch axis, say so directly. Unless an API
32+
documents an exception, batch sizes must match exactly and batch broadcasting
33+
is not supported.
34+
35+
Rank, batch, sparse, and dense dimensions
36+
------------------------------------------
37+
38+
These terms are not interchangeable:
39+
40+
* **Tensor rank** is ``tensor.ndim``, the total number of logical axes.
41+
* A **batch dimension** indexes independent problem instances. Repository
42+
convention is one leading batch axis.
43+
* A **sparse dimension** has coordinates represented by sparse indices. For
44+
COO, ``sparse_dim()`` is the number of leading indexed dimensions. CSR and
45+
CSC always have two sparse matrix dimensions.
46+
* A **dense dimension of a sparse tensor** is a trailing dimension stored in
47+
each specified value. It is not the same as a dense, ``torch.strided``
48+
tensor. Most matrix operations here require ``dense_dim() == 0`` unless
49+
documented otherwise.
50+
51+
For example, a hybrid sparse matrix with shape
52+
``(n_rows, n_cols, feature_size)`` can have two sparse dimensions and one
53+
dense feature dimension. A true three-dimensional sparse object, such as a
54+
sparse volume ``(depth, height, width)``, is different from a rank-3 tensor
55+
whose leading axis is a batch of matrices.
56+
57+
COO and compressed-layout batching
58+
----------------------------------
59+
60+
PyTorch represents batching differently for COO and compressed layouts, so
61+
documentation must preserve that distinction.
62+
63+
For COO, a tensor of shape ``(batch_size, n_rows, n_cols)`` commonly has three
64+
sparse dimensions. PyTorch does not label the first one as a batch dimension;
65+
an API in this package *logically interprets* it as one. COO batch items may
66+
have different numbers of specified entries, including zero.
67+
68+
For CSR and CSC, PyTorch represents tensors as
69+
``(*batch_shape, n_rows, n_cols, *dense_shape)``. The normal form supported by
70+
this package is ``(batch_size, n_rows, n_cols)``. Batched CSR and CSC require
71+
the same number of specified entries in every batch item; that is a storage
72+
constraint, not a mathematical requirement of batching. Do not generalise
73+
this restriction to COO.
74+
75+
``_nnz()`` for batched gradients
76+
--------------------------------
77+
78+
``_nnz()`` is layout-specific for a batched sparse tensor. In particular, the
79+
gradient produced for the sparse input of :func:`torchsparsegradutils.sparse_mm`
80+
is covered by ``test_sprase_mm_backward`` in the test suite:
81+
82+
* For batched CSR, ``A.grad._nnz()`` is the number of specified entries
83+
**per batch element**.
84+
* For batched COO, ``A.grad._nnz()`` is the number of specified entries in the
85+
**whole tensor**.
86+
87+
Thus, if every batch item has ``nse_per_matrix`` specified entries and the
88+
batch size is ``batch_size``, the checked invariants are:
89+
90+
.. code-block:: python
91+
92+
# Batched CSR: PyTorch reports a per-batch-item count.
93+
assert A_csr.grad._nnz() == nse_per_matrix
94+
95+
# Batched COO: PyTorch reports a count over the complete tensor.
96+
assert A_coo.grad._nnz() == nse_per_matrix * batch_size
97+
98+
This does not make CSR's count more local mathematically; it describes the
99+
storage-count API exposed by PyTorch. For an unbatched COO or CSR matrix,
100+
``_nnz()`` is simply the count for that one matrix.
101+
102+
Specified entries, explicit zeros, and ``nnz``
103+
----------------------------------------------
104+
105+
Prefer **specified entry** and **number of specified elements** (``nse``) when
106+
the distinction matters. Sparse storage can contain an explicit value equal to
107+
zero, so “number of nonzeros” can be misleading.
108+
109+
* An **explicit zero** is a stored value equal to zero.
110+
* A **structural** or **implicit zero** is an absent coordinate interpreted as
111+
the sparse fill value.
112+
* Existing code and benchmarks may use ``nnz``. When retained, define it as
113+
the count of stored/specified entries, not necessarily mathematically
114+
nonzero values.
115+
116+
Avoid calling ``values()`` “nonzero values” unless explicit zeros are known to
117+
be absent; use “stored values” or “explicit values” instead.
118+
119+
Layout and index names
120+
----------------------
121+
122+
Use PyTorch's layout names consistently: sparse COO (``torch.sparse_coo``),
123+
sparse CSR (``torch.sparse_csr``), sparse CSC (``torch.sparse_csc``), sparse
124+
BSR (``torch.sparse_bsr``), sparse BSC (``torch.sparse_bsc``), and dense or
125+
strided (``torch.strided``). Prefer **layout** over mixing “format” and
126+
“layout” in the same API description. “Compressed sparse layout” can refer to
127+
CSR, CSC, BSR, and BSC collectively.
128+
129+
Name index arrays after the represented axis:
130+
131+
* COO: ``batch_indices``, ``row_indices``, and ``col_indices``.
132+
* CSR: ``crow_indices``, ``col_indices``, and ``values``.
133+
* CSC: ``ccol_indices``, ``row_indices``, and ``values``.
134+
135+
``crow_indices`` and ``ccol_indices`` are compressed pointers, not coordinate
136+
lists. When converting them to coordinates, say **uncompress**, **expand**, or
137+
**repeat-interleave the row/column indices**.
138+
139+
Shape symbols and operations
140+
----------------------------
141+
142+
Within a function or document, choose one scheme and keep it consistent:
143+
``batch_size`` (or ``B``), ``nrows``/``n_rows`` (or ``M``),
144+
``ncols``/``n_cols`` (or ``N``), and ``nrhs``/``n_rhs`` (or ``K``). Short local
145+
names such as ``b, nrows, ncols`` are appropriate in compact numerical code.
146+
147+
For multiplication, use:
148+
149+
.. code-block:: text
150+
151+
A: (n_rows, n_inner) or (batch_size, n_rows, n_inner)
152+
B: (n_inner, n_cols) or (batch_size, n_inner, n_cols)
153+
C: (n_rows, n_cols) or (batch_size, n_rows, n_cols)
154+
155+
For solves, use:
156+
157+
.. code-block:: text
158+
159+
A: (n, n) or (batch_size, n, n)
160+
B: (n, n_rhs) or (batch_size, n, n_rhs)
161+
X: same shape as B
162+
163+
Call ``B`` the **right-hand side**; its final axis is the number of
164+
right-hand sides, not a batch dimension. ``A.T`` or
165+
``A.transpose(-2, -1)`` means the matrix transpose of the final two axes and
166+
leaves a batch axis in place.
167+
168+
Reduction, distribution, and memory wording
169+
--------------------------------------------
170+
171+
For ``A`` with shape ``(n_rows, n_cols)``, “reduce over rows” means reduce
172+
axis 0 and produce one value per column; “reduce over columns” means reduce
173+
axis 1 and produce one value per row. Avoid “row-wise” and “column-wise”
174+
unless the reduced and retained axes are both stated.
175+
176+
For ``torch.distributions``-compatible code, use
177+
``sample_shape + batch_shape + event_shape``. Sample dimensions are not batch
178+
dimensions. Spatial axes, channels, and batch axes are also distinct; use
179+
``spatial_dims``, ``spatial_shape``, ``num_channels``, and ``batch_size`` as
180+
appropriate and document a concrete volume ordering such as
181+
``(depth, height, width)``.
182+
183+
Use **view** only when storage is shared, **copy** when storage is newly
184+
allocated, and **reshape** when either may occur. “Materialise dense” means
185+
constructing a full dense tensor, for example with ``to_dense()``. Do not claim
186+
that an operation has no allocation merely because an input was expanded:
187+
``stack``, ``cat``, ``clone``, ``contiguous``, and output creation may allocate.
188+
189+
COO coalescing and validation messages
190+
--------------------------------------
191+
192+
Only COO tensors are **coalesced** or **uncoalesced**. Coalesced COO has unique,
193+
sorted coordinates; uncoalesced COO can have duplicate coordinates that sum at
194+
the same logical position. Coalesce before nonlinear operations on values
195+
unless the implementation has proved an equivalent treatment of duplicates.
196+
Do not apply this terminology to CSR or CSC.
197+
198+
Shape-validation errors should describe the accepted logical forms as well as
199+
the received shape:
200+
201+
.. code-block:: python
202+
203+
raise ValueError(
204+
"A must be an unbatched sparse matrix with shape (n_rows, n_cols) "
205+
"or a batched sparse matrix with shape (batch_size, n_rows, n_cols); "
206+
f"got shape {tuple(A.shape)}."
207+
)
208+
209+
Before submitting shape-related changes, make sure public inputs and outputs
210+
state explicit shapes; distinguish batch, matrix, spatial, channel, sample,
211+
and event axes; and separate COO-specific from compressed-layout-specific
212+
constraints.
213+
214+
References
215+
----------
216+
217+
* `PyTorch sparse tensor documentation <https://docs.pytorch.org/docs/stable/sparse.html>`_
218+
* `PyTorch distributions documentation <https://docs.pytorch.org/docs/stable/distributions.html>`_
219+
* `PyTorch tensor views documentation <https://docs.pytorch.org/docs/stable/tensor_view.html>`_

0 commit comments

Comments
 (0)