Skip to content

Spatial structure

A graph is already spatial in the topological sense: its edges say which nodes can interact. Metric space adds positions, displacement, and distance without changing that connectivity.

The contract

Keep the four facts separate:

topology             G = (source, target)
coordinate frame     CRS, origin, axes, units
position             P[N, D]
time-varying field   X[T, N, F]

For each edge e = u -> v, derive geometry in the graph's original COO order:

source position      P_u
target position      P_v
displacement         delta_e = P_v - P_u       [E, D]
distance             r_e = ||delta_e||          [E]

The current public API can already express a radial spatial message:

from tinygrad import Tensor
from tinymesh import Graph

graph = Graph(3, source=[0, 1], target=[1, 2])
position = Tensor([[0.0, 0.0], [3.0, 4.0], [3.0, 8.0]])
state = Tensor([[1.0], [2.0], [3.0]])

source = graph.edge_values(position, endpoint="source")
target = graph.edge_values(position, endpoint="target")
delta = target - source
distance = (delta * delta).sum(axis=-1).sqrt()
weight = 1 / (1 + distance)
output = graph.sum(state, edge_weight=weight)

Graph owns sparse edge identity and aggregation. Position is an ordinary tinygrad node field. The coordinate frame belongs to the data boundary because a tensor cannot say whether [3, 4] means degrees, metres, or a local simulation frame.

For a utility network, physical pipe connectivity should remain authoritative. Two pipes crossing on a map are not connected unless the source network says they share a junction. Radius or nearest-neighbor topology is useful when connectivity is absent, not as a silent replacement for domain topology.

Space and time are orthogonal

The first useful spatiotemporal form is:

fixed topology G
static position P[N, D]
node history X[T, N, F]
           |
           v
edge geometry -> spatial message -> temporal cell

A moving mesh changes position to P[T, N, D]. A changing network changes topology to G_t. These are different contracts:

static geometry      G,   P[N, D],    X[T, N, F]
moving geometry      G,   P[T, N, D], X[T, N, F]
changing topology    G_t, P_t,         X_t

The current fixed-graph signal covers only the first topology case. A caller can slice moving positions one snapshot at a time, but Tinymesh does not yet own their temporal alignment.

Reference implementations

The pinned sources answer three different questions.

PyTorch Geometric: graph geometry

At revision 726310a, PyG keeps position in data.pos:

That decomposition matches Tinymesh: topology, node position, and derived edge geometry remain distinct. It does not require a PyG-compatible data container.

TorchGeo: geospatial alignment

At revision 468c670, TorchGeo's GeoDataset owns coordinate reference system, resolution, bounds, and spatiotemporal queries. Its GeoSampler selects regions in that coordinate space.

This is a data-alignment reference, not a graph-compute reference. Tinymesh should accept already aligned numeric positions; a geospatial adapter should project longitude and latitude into an appropriate metric frame before Euclidean distance enters a model. The TorchGeo paper describes the dataset, sampler, and multispectral-data boundary.

TerraTorch: model composition

At revision 375356c, TerraTorch's EncoderDecoderFactory composes a backbone, optional necks, decoder, and task head.

That separation is useful when Tinymesh has several proven interchangeable model parts. It does not justify a registry or factory before those callers exist, and it does not define spatial graph semantics. The TerraTorch paper describes the wider fine-tuning and benchmarking toolkit.

Research lineage

  • DCRNN treats directed network diffusion as the spatial operator and recurrence as the temporal operator.
  • E(n)-equivariant GNNs show how coordinate differences and radial distances can preserve translation, rotation, and reflection symmetries.
  • MeshGraphNets uses message passing over a simulation mesh and predicts physical dynamics.
  • MultiScale MeshGraphNets adds coarse connectivity when spatially close points remain far apart in fine-mesh graph distance.

These papers describe progressively stronger needs. Scalar distance weighting does not provide directional equivariance, mesh cells, adaptive remeshing, or multiscale propagation.

Decision

Add no public spatial type yet:

Graph                         owns topology
Tensor[N, D]                  owns numeric position
data adapter                  owns CRS, projection, and units
edge_values + tensor math     derive COO edge geometry
Graph.sum                     aggregates scalar-weighted node messages

The spatial geometry experiment now proves that direct composition on one fixed graph preserves COO identity, sparse intermediate shapes, first-order gradients, vertex relabeling, translation, and rotation contracts on CPU and Metal. A vector edge-message aggregation primitive earns its place only when directional displacement is a real caller; a Mesh type earns its place only when faces, cells, or hierarchy need an owner.