Skip to main content

Gridification for point clouds in any dimension (fast greedy optimal transport through Cartesian Sort).

Project description

Open In Colab PyPI version Documentation Status GitHub

❒ SquareNet — Bijective Gridification of Point Clouds

SquareNet maps unstructured point clouds to structured grids through a bijective transformation: one point, one cell, no overlap, fully invertible.

The practical payoff: you replace expensive spatial queries (k-NN, radius search, neighborhood graphs) with plain tensor indexing. Think of it as an alternative to kd-trees, voxelization, rasterization, or graph-based approaches, but with a regular tensor structure allowing for massive parallelisation.

✔ Works in any dimension
✔ Handles non-convex geometries and irregular distributions
✔ Scales to millions of points (seconds, not minutes)
✔ Compatible with PyTorch and JAX
✔ Native pading for mismatch between number of grid slots and number of points (since version 1.2)


Example of gridification

Raw

🚀 How it works

You initialize SquareNet with a target grid shape, then call fit() on your point cloud. Under the hood, the Cartesian sort algorithm rearranges point indices into structured grid multi-indices.

raw points      #(N, D)       →  sn.fit(X)        →  grid  #(N1, N2, ..., ND)
flat data       #(N, *C)      →  sn.map(X)        →  structured tensor  #(N1, ..., ND, *C)
structured tensor             →  sn.invert_map(X) →  back to flat view

The mapping is bijective, so invert_map is exact — no information lost.


⚙️ The Cartesian Sort Algorithm

General Optimal Transport (the theoretically correct solution to gridification) is O(N²) to O(N³) — intractable at scale. Cartesian sort is a fast heuristic that exploits the tensor structure of the grid to sidestep that complexity.

The idea is simple: loop over 1D Cartesian projections of the point cloud (x, y, z, ...) and sort points along the corresponding grid axis (rows, columns, ...). Each 1D sort is O(N log N) and fully vectorized. Since sorting along axis i+1 partially undoes the ordering along axis i, you repeat the full loop until all axes are sorted simultaneously — typically fewer than 50 iterations.

What you get:

  • Speed. ⏱️ Millions of points in seconds. All operations are native tensor ops.
  • Coordinate monotonicity. x increases along rows, y along columns, etc. This enable potential N-dimensional dychotomy principle for certain algorithms.
  • Approximate neighborhood preservation. Points close in space land close in the grid. Concrete experimental results on a 1M-point 2D dataset (France map, method='fast'):
    • Requesting a 11×11 square window ([i-5:i+6, j-5:j+6] = 0.01% of candidates) → recovers ~97% of true nearest neighbors
    • Requesting a 31×31 square window ([i-15:i+16, j-15:j+16] = 0.1% of candidates) → recovers ~99.5%
  • Volume conservation. Bijectivity naturally leads to conservation of volumes (or more generally measures: $\int \rho(x), dV$ when density rho is not constant in the point cloud). By conservation of volume, it is not mean that $$\mathrm{vol}(g(A), g(B), g(C)) = \mathrm{vol}(A, B, C)$$ where $ABC$ is a triangle, since the image of a triangle is generally not a triangle anymore. It would be equal to $$\mathrm{vol}(\Omega),\qquad \Omega = { g(X) \mid X \in ABC }$$

What you don't get:

  • Optimal Transport. SquareNet trades exactness for speed. If you need the provably optimal assignment, this isn't the right tool.
  • Reverse neighborhood preservation. Close in space → close in grid, but not the other way around. Holes, clusters, and gaps in your data will be "closed" by the grid, which can place unrelated points next to each other.
  • Angular preservation. Volume and angles can't both be conserved in the general case (classical result). Expect some distortion, especially near boundaries.

Three fitting modes

Mode How it works When to use
fast (default) Raw Cartesian sort General use, large datasets
robust Sorts subgrids at each step Less prone to local minima
ultimate Adds random shearing perturbations Near-zero outliers, but slow and require tuning max_iter parameter (bigger = better, but depending on scale and geometry sometimes 100 iterations are fine, sometimes best results require 10_000 iterations = 20 minutes fit for 1-M points dataset)

📦 Installation

pip install squarenet

🧠 Quick Start

→ See 00_getting_started.ipynb

from squarenet import SquareNet
import numpy as np

# 4D example: N points → 5×11×7×13 grid
N = 5 * 11 * 7 * 13
D = 4
X = np.random.rand(N, D)

sn = SquareNet(gridshape=(5, 11, 7, 13))
sn.fit(X)

# Inspect grid quality
sn.neighbormap()

# Map point data to grid and back
Xgrid = sn.map(X)         # shape (5, 11, 7, 13, D)
Xback = sn.invert_map(Xgrid)      # == X

Approximate nearest-neighbor search

point = np.random.rand(D)
index = sn.search_sorted(point)   # fast N-dimensional generalisation of  1D search sorted,
#will return a cell multi-index that best fit the given point (approximate method).

Index conversions

Working on a subset? mapidx converts flat point indices to grid multi-indices and back.

# Select points inside a disk, map their indices to the grid
sel = np.where(points[:, 0]**2 + points[:, 1]**2 <= 100) #raw indexes
gridsel = sn.mapidx(sel) #grid indexes
selback = sn.invert_mapidx(np.stack(gridsel, axis=1)) # == sel

Visualizing the mapping

sn = SquareNet(gridshape=(400, 400))
sn.fit("france")
sn.plot()

📈 When to use SquareNet

  • Point cloud processing — fast spatial queries (neighbors, subsets) with contiguous memory lookup and vectorized tensorial processing instead of irregular kd-tree/voxels data structure
  • Kernel methods — efficient approximation of large Gram matrices via the grid structure
  • Deep learning — convert flat point datasets into tensors ready for CNNs or attention-based models

License: MIT | Author: ArmanddeCacqueray

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

squarenet-1.3.1.tar.gz (33.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

squarenet-1.3.1-py3-none-any.whl (35.8 kB view details)

Uploaded Python 3

File details

Details for the file squarenet-1.3.1.tar.gz.

File metadata

  • Download URL: squarenet-1.3.1.tar.gz
  • Upload date:
  • Size: 33.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for squarenet-1.3.1.tar.gz
Algorithm Hash digest
SHA256 fe11f3da6ab4703f03a5859fba1936f585b1712b063f272a03d16c69a6e57b40
MD5 0315292396fb9cbddd74336f03faad40
BLAKE2b-256 c03bf2dfde9e91eaee8d0555e321d4c7e21db274246bc44b3c4981dd6ea6c261

See more details on using hashes here.

File details

Details for the file squarenet-1.3.1-py3-none-any.whl.

File metadata

  • Download URL: squarenet-1.3.1-py3-none-any.whl
  • Upload date:
  • Size: 35.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for squarenet-1.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 7a8adfcd2d9ba9ab087a0bd322e992c14e554f6e7482c586365ceb0b9ab04761
MD5 b69c249cb6812d74bb0afb9394bc723b
BLAKE2b-256 37a9f06f43c182c5520ed3f0f27e74076157dc3487c231c9b0a7a6c87f538273

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page