gridpoints 1.0.3__tar.gz → 1.0.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gridpoints-1.0.3 → gridpoints-1.0.5}/PKG-INFO +27 -21
- {gridpoints-1.0.3 → gridpoints-1.0.5}/README.md +28 -22
- {gridpoints-1.0.3 → gridpoints-1.0.5}/pyproject.toml +1 -1
- {gridpoints-1.0.3 → gridpoints-1.0.5}/src/griddemo/__init__.py +1 -1
- {gridpoints-1.0.3 → gridpoints-1.0.5}/src/griddemo/quick_demo.py +3 -3
- {gridpoints-1.0.3 → gridpoints-1.0.5}/src/gridpoints/__init__.py +1 -1
- {gridpoints-1.0.3 → gridpoints-1.0.5}/src/gridpoints/array_libs.py +4 -4
- {gridpoints-1.0.3 → gridpoints-1.0.5}/src/gridpoints/mlg_sort.py +22 -25
- {gridpoints-1.0.3 → gridpoints-1.0.5}/src/gridpoints.egg-info/PKG-INFO +27 -21
- {gridpoints-1.0.3 → gridpoints-1.0.5}/LICENSE +0 -0
- {gridpoints-1.0.3 → gridpoints-1.0.5}/setup.cfg +0 -0
- {gridpoints-1.0.3 → gridpoints-1.0.5}/src/gridpoints/diagonal_tricks.py +0 -0
- {gridpoints-1.0.3 → gridpoints-1.0.5}/src/gridpoints/kdtree.py +0 -0
- {gridpoints-1.0.3 → gridpoints-1.0.5}/src/gridpoints.egg-info/SOURCES.txt +0 -0
- {gridpoints-1.0.3 → gridpoints-1.0.5}/src/gridpoints.egg-info/dependency_links.txt +0 -0
- {gridpoints-1.0.3 → gridpoints-1.0.5}/src/gridpoints.egg-info/requires.txt +0 -0
- {gridpoints-1.0.3 → gridpoints-1.0.5}/src/gridpoints.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gridpoints
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.5
|
|
4
4
|
Summary: fast multidimensional sorting tool for point clouds using the monotonic lagrangian grid
|
|
5
5
|
Author: Armand de Cacqueray
|
|
6
6
|
License: MIT
|
|
@@ -16,25 +16,31 @@ Dynamic: license-file
|
|
|
16
16
|
|
|
17
17
|
## ❒ Gridpoints — Multi dimensional sort for point clouds
|
|
18
18
|
|
|
19
|
-
`Gridpoints` maps unstructured **point clouds** to structured grids through a **bijective transformation**: one point, one cell, no overlap, fully invertible. It replaces and enhances the squarenet project with a
|
|
19
|
+
`Gridpoints` maps unstructured **point clouds** to structured grids through a **bijective transformation**: one point, one cell, no overlap, fully invertible. It replaces and enhances the squarenet project with a more powerfull sorting algorithm.
|
|
20
20
|
|
|
21
|
-
Take raw point cloud `
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
What it does: Take raw point cloud `P(N, D)` and find a grid shape and an index permutation `order` such that Pgrid = P[order].reshape(*gridshape, D)
|
|
22
|
+
is sorted along every axis of the grid. E.g. in 3D, for Pgrid = (x, y, z):
|
|
23
|
+
```text
|
|
24
|
+
x[i+1, j, k] >= x[i, j, k]
|
|
25
|
+
y[i, j+1, k] >= y[i, j, k]
|
|
26
|
+
z[i, j, k+1] >= z[i, j, k]
|
|
27
|
+
```
|
|
28
|
+
→ On the `Pgrid` view of `P`, neighbor queries become a simple stencil look-up :
|
|
29
|
+
```text
|
|
30
|
+
neighborhood[i, j, k] = {Pgrid[i±di, j±dj, k±dk] | (di, dj, dk) ≤ R},
|
|
31
|
+
````
|
|
32
|
+
where `R` is a radius cutoff to determine, allowing local operations in linear time.
|
|
33
|
+
→ Standard operations (convolution, clustering, …) can then be applied
|
|
34
|
+
directly on the `Pgrid` view instead of relying on complex graph convolutions
|
|
29
35
|
or other point-cloud techniques.
|
|
30
36
|
|
|
31
|
-
`
|
|
37
|
+
`P` can be a NumPy, PyTorch or CuPy array of any dimension (N, D).
|
|
32
38
|
To allow natural padding when the grid has more cells than points,
|
|
33
39
|
NaNs and Infs are supported in a consistent manner:
|
|
34
40
|
- nans → random position
|
|
35
41
|
- (+-) infs → border of the grid
|
|
36
|
-
|
|
37
|
-
deal with void/special grid cells.
|
|
42
|
+
|
|
43
|
+
This allow to gridsort prime or variable number of points N, as long as one is ready to deal with void/special grid cells.
|
|
38
44
|
|
|
39
45
|
Expected runtime for sorting 1 million points: CPU → < 10s, GPU → < 500 ms
|
|
40
46
|
|
|
@@ -54,20 +60,20 @@ import gridpoints as grid
|
|
|
54
60
|
import numpy as np
|
|
55
61
|
|
|
56
62
|
# Raw point cloud (numpy, pytorch or cupy)
|
|
57
|
-
|
|
63
|
+
A = np.random.rand(1_000_000, 3)
|
|
58
64
|
|
|
59
65
|
# Sorted view: place the points inside the grid
|
|
60
|
-
order = grid.argsort(
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
order = grid.argsort(A, gridshape=(100, 100, 100))
|
|
67
|
+
Bflat = A[order]
|
|
68
|
+
Bgrid = Bflat.reshape(100, 100, 100, 3)
|
|
63
69
|
|
|
64
70
|
# Rest of your pipeline, working with grids
|
|
65
|
-
|
|
71
|
+
Cgrid = apply_something(Bgrid)
|
|
66
72
|
|
|
67
73
|
# Back to the original points indexing
|
|
68
|
-
|
|
74
|
+
Cflat = Cgrid.reshape(-1, 3)
|
|
69
75
|
orderinv = grid.invert_permutation(order)
|
|
70
|
-
|
|
76
|
+
C = Cflat[orderinv] # matches the initial points order
|
|
71
77
|
```
|
|
72
78
|
|
|
73
79
|
<img src="https://raw.githubusercontent.com/Neighborhood-Grid/CubeNet/main/ballexemple.png">
|
|
@@ -85,6 +91,6 @@ As an example, empirical results in 2-D show that `R = 5` is enough for ~99 % of
|
|
|
85
91
|
The typical use-case of `Gridpoints` is to allow fast local operations on arbitrary point clouds using stencil kernels:
|
|
86
92
|
|
|
87
93
|
```text
|
|
88
|
-
output(i, j, k) = f(
|
|
94
|
+
output(i, j, k) = f( Pgrid[i±di, j±dj, k±dk] | di, dj, dk in local window )
|
|
89
95
|
```
|
|
90
96
|
To go beyond standard (slow) python loops, this can be accelerated with native grid convolution operations of standard libraries whenever possible, or with `pystencils` or `taichi` compilers for complex/non linear grid kernels.
|
|
@@ -1,24 +1,30 @@
|
|
|
1
1
|
## ❒ Gridpoints — Multi dimensional sort for point clouds
|
|
2
2
|
|
|
3
|
-
`Gridpoints` maps unstructured **point clouds** to structured grids through a **bijective transformation**: one point, one cell, no overlap, fully invertible. It replaces and enhances the squarenet project with a
|
|
4
|
-
|
|
5
|
-
Take raw point cloud `
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
`Gridpoints` maps unstructured **point clouds** to structured grids through a **bijective transformation**: one point, one cell, no overlap, fully invertible. It replaces and enhances the squarenet project with a more powerfull sorting algorithm.
|
|
4
|
+
|
|
5
|
+
What it does: Take raw point cloud `P(N, D)` and find a grid shape and an index permutation `order` such that Pgrid = P[order].reshape(*gridshape, D)
|
|
6
|
+
is sorted along every axis of the grid. E.g. in 3D, for Pgrid = (x, y, z):
|
|
7
|
+
```text
|
|
8
|
+
x[i+1, j, k] >= x[i, j, k]
|
|
9
|
+
y[i, j+1, k] >= y[i, j, k]
|
|
10
|
+
z[i, j, k+1] >= z[i, j, k]
|
|
11
|
+
```
|
|
12
|
+
→ On the `Pgrid` view of `P`, neighbor queries become a simple stencil look-up :
|
|
13
|
+
```text
|
|
14
|
+
neighborhood[i, j, k] = {Pgrid[i±di, j±dj, k±dk] | (di, dj, dk) ≤ R},
|
|
15
|
+
````
|
|
16
|
+
where `R` is a radius cutoff to determine, allowing local operations in linear time.
|
|
17
|
+
→ Standard operations (convolution, clustering, …) can then be applied
|
|
18
|
+
directly on the `Pgrid` view instead of relying on complex graph convolutions
|
|
13
19
|
or other point-cloud techniques.
|
|
14
20
|
|
|
15
|
-
`
|
|
21
|
+
`P` can be a NumPy, PyTorch or CuPy array of any dimension (N, D).
|
|
16
22
|
To allow natural padding when the grid has more cells than points,
|
|
17
23
|
NaNs and Infs are supported in a consistent manner:
|
|
18
24
|
- nans → random position
|
|
19
25
|
- (+-) infs → border of the grid
|
|
20
|
-
|
|
21
|
-
deal with void/special grid cells.
|
|
26
|
+
|
|
27
|
+
This allow to gridsort prime or variable number of points N, as long as one is ready to deal with void/special grid cells.
|
|
22
28
|
|
|
23
29
|
Expected runtime for sorting 1 million points: CPU → < 10s, GPU → < 500 ms
|
|
24
30
|
|
|
@@ -38,20 +44,20 @@ import gridpoints as grid
|
|
|
38
44
|
import numpy as np
|
|
39
45
|
|
|
40
46
|
# Raw point cloud (numpy, pytorch or cupy)
|
|
41
|
-
|
|
47
|
+
A = np.random.rand(1_000_000, 3)
|
|
42
48
|
|
|
43
49
|
# Sorted view: place the points inside the grid
|
|
44
|
-
order = grid.argsort(
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
order = grid.argsort(A, gridshape=(100, 100, 100))
|
|
51
|
+
Bflat = A[order]
|
|
52
|
+
Bgrid = Bflat.reshape(100, 100, 100, 3)
|
|
47
53
|
|
|
48
54
|
# Rest of your pipeline, working with grids
|
|
49
|
-
|
|
55
|
+
Cgrid = apply_something(Bgrid)
|
|
50
56
|
|
|
51
57
|
# Back to the original points indexing
|
|
52
|
-
|
|
58
|
+
Cflat = Cgrid.reshape(-1, 3)
|
|
53
59
|
orderinv = grid.invert_permutation(order)
|
|
54
|
-
|
|
60
|
+
C = Cflat[orderinv] # matches the initial points order
|
|
55
61
|
```
|
|
56
62
|
|
|
57
63
|
<img src="https://raw.githubusercontent.com/Neighborhood-Grid/CubeNet/main/ballexemple.png">
|
|
@@ -69,6 +75,6 @@ As an example, empirical results in 2-D show that `R = 5` is enough for ~99 % of
|
|
|
69
75
|
The typical use-case of `Gridpoints` is to allow fast local operations on arbitrary point clouds using stencil kernels:
|
|
70
76
|
|
|
71
77
|
```text
|
|
72
|
-
output(i, j, k) = f(
|
|
78
|
+
output(i, j, k) = f( Pgrid[i±di, j±dj, k±dk] | di, dj, dk in local window )
|
|
73
79
|
```
|
|
74
|
-
To go beyond standard (slow) python loops, this can be accelerated with native grid convolution operations of standard libraries whenever possible, or with `pystencils` or `taichi` compilers for complex/non linear grid kernels.
|
|
80
|
+
To go beyond standard (slow) python loops, this can be accelerated with native grid convolution operations of standard libraries whenever possible, or with `pystencils` or `taichi` compilers for complex/non linear grid kernels.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "gridpoints"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.5"
|
|
8
8
|
description = "fast multidimensional sorting tool for point clouds using the monotonic lagrangian grid "
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -108,14 +108,14 @@ def print_zoom(Ygrid, center, side=1):
|
|
|
108
108
|
else:
|
|
109
109
|
i, j, k = center
|
|
110
110
|
print("axis 0:")
|
|
111
|
-
for ii in range(i-
|
|
111
|
+
for ii in range(i-side, i+side+1):
|
|
112
112
|
print(_format_point(Ygrid[ii, j, k]), end=" ")
|
|
113
113
|
print()
|
|
114
114
|
print("axis 1:")
|
|
115
|
-
for jj in range(j-
|
|
115
|
+
for jj in range(j-side, j+side+1):
|
|
116
116
|
print(_format_point(Ygrid[i, jj, k]), end=" ")
|
|
117
117
|
print()
|
|
118
118
|
print("axis 2:")
|
|
119
|
-
for kk in range(k-
|
|
119
|
+
for kk in range(k-side, k+side+1):
|
|
120
120
|
print(_format_point(Ygrid[i, j, kk]), end=" ")
|
|
121
121
|
print()
|
|
@@ -10,7 +10,7 @@ like arange, copy, shuffle etc.
|
|
|
10
10
|
_FLOAT = 'float32'
|
|
11
11
|
_INT = 'int32'
|
|
12
12
|
|
|
13
|
-
EPS = 10**(-10)
|
|
13
|
+
EPS = 10**(-5) if (_FLOAT == 'float32') else 10**(-10)
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
def get_dtype(dtp, xp):
|
|
@@ -100,8 +100,8 @@ def nan_to_num(arr):
|
|
|
100
100
|
finite = xp.isfinite(x)
|
|
101
101
|
vals = x[finite]
|
|
102
102
|
|
|
103
|
-
out[xp.isposinf(x), d] =
|
|
104
|
-
out[xp.isneginf(x), d] =
|
|
103
|
+
out[xp.isposinf(x), d] = vals.max() + 1
|
|
104
|
+
out[xp.isneginf(x), d] = vals.min() - 1
|
|
105
105
|
|
|
106
106
|
nan = xp.isnan(x)
|
|
107
107
|
n = nan.sum().item() if backend == 'torch' else int(nan.sum())
|
|
@@ -164,4 +164,4 @@ def transpose(X, axes):
|
|
|
164
164
|
_, name = get_backend(X)
|
|
165
165
|
if name == "torch":
|
|
166
166
|
return X.permute(*axes)
|
|
167
|
-
return X.transpose(axes)
|
|
167
|
+
return X.transpose(axes)
|
|
@@ -18,7 +18,7 @@ from .kdtree import kdtree_order
|
|
|
18
18
|
# Sorting API
|
|
19
19
|
# ============================================
|
|
20
20
|
|
|
21
|
-
def argsort(X, gridshape, level=2, init="kdtree", n_iter=
|
|
21
|
+
def argsort(X, gridshape, verbose = 2, level=2, init="kdtree", n_iter=40):
|
|
22
22
|
"""Compute a multi-directional monotonic sorted ordering of a point cloud.
|
|
23
23
|
|
|
24
24
|
Parameters
|
|
@@ -33,6 +33,8 @@ def argsort(X, gridshape, level=2, init="kdtree", n_iter=50, verbose = 2):
|
|
|
33
33
|
Note: Point clouds can be padded with +/- infinity placeholders to reach
|
|
34
34
|
a valid grid size. NaNs can also be used, but will be placed randomly
|
|
35
35
|
rather than pushed to grid boundaries.
|
|
36
|
+
verbose : int, default 2
|
|
37
|
+
0 = silent, 1 = log progress, 2 = 1 + warn if convergence failed
|
|
36
38
|
level : {1, 2, 3}, default=2
|
|
37
39
|
Maximum order of lattice directions considered. Higher values improve
|
|
38
40
|
grid quality at the expense of computational speed. Note that `level=3`
|
|
@@ -40,19 +42,14 @@ def argsort(X, gridshape, level=2, init="kdtree", n_iter=50, verbose = 2):
|
|
|
40
42
|
init : {'kdtree', 'shuffle'} or None, default="kdtree"
|
|
41
43
|
Initialization method for the permutation order. Try init = None
|
|
42
44
|
if the point cloud is already nearly sorted.
|
|
43
|
-
n_iter : int, default=
|
|
45
|
+
n_iter : int, default=40
|
|
44
46
|
Maximum number of optimization iterations.
|
|
45
|
-
verbose : int, default 2
|
|
46
|
-
0 = silent, 1 = log progress, 2 = 1 + warn if convergence failed
|
|
47
47
|
|
|
48
48
|
Returns
|
|
49
49
|
-------
|
|
50
50
|
order : ndarray of shape (N,)
|
|
51
51
|
Permutation indices such that `X[order].reshape(*shape, D)` forms a
|
|
52
52
|
monotonically sorted grid.
|
|
53
|
-
converged : bool
|
|
54
|
-
`True` if the algorithm converged within `n_iter` iterations, `False` otherwise.
|
|
55
|
-
If not converged, the fonction will also raise a convergence warning
|
|
56
53
|
"""
|
|
57
54
|
N, D = X.shape
|
|
58
55
|
assert math.prod(gridshape) == N
|
|
@@ -65,9 +62,8 @@ def argsort(X, gridshape, level=2, init="kdtree", n_iter=50, verbose = 2):
|
|
|
65
62
|
order = kdtree_order(X, gridshape)
|
|
66
63
|
if verbose >=1:
|
|
67
64
|
print("done")
|
|
68
|
-
converged = True
|
|
69
65
|
if level == 1:
|
|
70
|
-
return order
|
|
66
|
+
return order
|
|
71
67
|
else:
|
|
72
68
|
order = arange(len(X), 'int', reference_array=X)
|
|
73
69
|
if init == "shuffle":
|
|
@@ -77,27 +73,28 @@ def argsort(X, gridshape, level=2, init="kdtree", n_iter=50, verbose = 2):
|
|
|
77
73
|
order = order[ordernew]
|
|
78
74
|
|
|
79
75
|
if not converged:
|
|
80
|
-
while level >= 2:
|
|
76
|
+
while level >= 2 and not converged:
|
|
81
77
|
level = level - 1
|
|
82
78
|
ordernew, converged = _mlg_step(
|
|
83
79
|
X[order], gridshape, level=level, n_iter=n_iter, verbose = verbose
|
|
84
80
|
)
|
|
85
81
|
order = order[ordernew]
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
82
|
+
if not converged and verbose >=2:
|
|
83
|
+
warnings.warn(
|
|
84
|
+
f"Algorithm failed to converge after {n_iter} iterations.\n " \
|
|
85
|
+
"Consider checking if the grid structure is already suitable \n" \
|
|
86
|
+
"for the given task, which is higly probable, or increase the \n" \
|
|
87
|
+
"`n_iter` parameter if strict convergence is required.\n",
|
|
88
|
+
"(to shutup this warning, run the sort with verbose <= 1)",
|
|
89
|
+
category=ConvergenceWarning,
|
|
90
|
+
stacklevel=2
|
|
91
|
+
)
|
|
95
92
|
if verbose:
|
|
96
93
|
print("done")
|
|
97
|
-
return order
|
|
94
|
+
return order
|
|
98
95
|
|
|
99
96
|
|
|
100
|
-
def sort(X, gridshape, level=2, init="kdtree", inplace=False, n_iter=
|
|
97
|
+
def sort(X, gridshape, verbose = 2, level=2, init="kdtree", inplace=False, n_iter=40):
|
|
101
98
|
"""Sort a point cloud `X` using the monotonic Lagrangian algorithm.
|
|
102
99
|
|
|
103
100
|
Parameters
|
|
@@ -112,6 +109,8 @@ def sort(X, gridshape, level=2, init="kdtree", inplace=False, n_iter=50, verbose
|
|
|
112
109
|
Note: Point clouds can be padded with +/- infinity placeholders to reach
|
|
113
110
|
a valid grid size. NaNs can also be used, but will be placed randomly
|
|
114
111
|
rather than pushed to grid boundaries.
|
|
112
|
+
verbose : int, default 2
|
|
113
|
+
0 = silent, 1 = log progress, 2 = 1 + warn if convergence failed
|
|
115
114
|
level : {1, 2, 3}, default=1
|
|
116
115
|
Maximum order of lattice directions considered. Higher values improve
|
|
117
116
|
grid quality at the expense of computational speed. Note that `level=3`
|
|
@@ -121,10 +120,8 @@ def sort(X, gridshape, level=2, init="kdtree", inplace=False, n_iter=50, verbose
|
|
|
121
120
|
if the point cloud is already nearly sorted.
|
|
122
121
|
inplace : bool, default=False
|
|
123
122
|
If True, modify the point cloud `X` in place.
|
|
124
|
-
n_iter : int, default=
|
|
123
|
+
n_iter : int, default=40
|
|
125
124
|
Maximum number of optimization iterations.
|
|
126
|
-
verbose : int, default 2
|
|
127
|
-
0 = silent, 1 = log progress, 2 = 1 + warn if convergence failed
|
|
128
125
|
|
|
129
126
|
Returns
|
|
130
127
|
-------
|
|
@@ -132,7 +129,7 @@ def sort(X, gridshape, level=2, init="kdtree", inplace=False, n_iter=50, verbose
|
|
|
132
129
|
The sorted point cloud such that `X_sorted.reshape(*shape, D)` forms a
|
|
133
130
|
monotonically sorted grid. Returns `None` if `inplace=True`.
|
|
134
131
|
"""
|
|
135
|
-
order
|
|
132
|
+
order = argsort(X, gridshape, verbose = verbose, level=level, init = init, n_iter=n_iter)
|
|
136
133
|
|
|
137
134
|
if inplace:
|
|
138
135
|
X[:] = X[order]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gridpoints
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.5
|
|
4
4
|
Summary: fast multidimensional sorting tool for point clouds using the monotonic lagrangian grid
|
|
5
5
|
Author: Armand de Cacqueray
|
|
6
6
|
License: MIT
|
|
@@ -16,25 +16,31 @@ Dynamic: license-file
|
|
|
16
16
|
|
|
17
17
|
## ❒ Gridpoints — Multi dimensional sort for point clouds
|
|
18
18
|
|
|
19
|
-
`Gridpoints` maps unstructured **point clouds** to structured grids through a **bijective transformation**: one point, one cell, no overlap, fully invertible. It replaces and enhances the squarenet project with a
|
|
19
|
+
`Gridpoints` maps unstructured **point clouds** to structured grids through a **bijective transformation**: one point, one cell, no overlap, fully invertible. It replaces and enhances the squarenet project with a more powerfull sorting algorithm.
|
|
20
20
|
|
|
21
|
-
Take raw point cloud `
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
What it does: Take raw point cloud `P(N, D)` and find a grid shape and an index permutation `order` such that Pgrid = P[order].reshape(*gridshape, D)
|
|
22
|
+
is sorted along every axis of the grid. E.g. in 3D, for Pgrid = (x, y, z):
|
|
23
|
+
```text
|
|
24
|
+
x[i+1, j, k] >= x[i, j, k]
|
|
25
|
+
y[i, j+1, k] >= y[i, j, k]
|
|
26
|
+
z[i, j, k+1] >= z[i, j, k]
|
|
27
|
+
```
|
|
28
|
+
→ On the `Pgrid` view of `P`, neighbor queries become a simple stencil look-up :
|
|
29
|
+
```text
|
|
30
|
+
neighborhood[i, j, k] = {Pgrid[i±di, j±dj, k±dk] | (di, dj, dk) ≤ R},
|
|
31
|
+
````
|
|
32
|
+
where `R` is a radius cutoff to determine, allowing local operations in linear time.
|
|
33
|
+
→ Standard operations (convolution, clustering, …) can then be applied
|
|
34
|
+
directly on the `Pgrid` view instead of relying on complex graph convolutions
|
|
29
35
|
or other point-cloud techniques.
|
|
30
36
|
|
|
31
|
-
`
|
|
37
|
+
`P` can be a NumPy, PyTorch or CuPy array of any dimension (N, D).
|
|
32
38
|
To allow natural padding when the grid has more cells than points,
|
|
33
39
|
NaNs and Infs are supported in a consistent manner:
|
|
34
40
|
- nans → random position
|
|
35
41
|
- (+-) infs → border of the grid
|
|
36
|
-
|
|
37
|
-
deal with void/special grid cells.
|
|
42
|
+
|
|
43
|
+
This allow to gridsort prime or variable number of points N, as long as one is ready to deal with void/special grid cells.
|
|
38
44
|
|
|
39
45
|
Expected runtime for sorting 1 million points: CPU → < 10s, GPU → < 500 ms
|
|
40
46
|
|
|
@@ -54,20 +60,20 @@ import gridpoints as grid
|
|
|
54
60
|
import numpy as np
|
|
55
61
|
|
|
56
62
|
# Raw point cloud (numpy, pytorch or cupy)
|
|
57
|
-
|
|
63
|
+
A = np.random.rand(1_000_000, 3)
|
|
58
64
|
|
|
59
65
|
# Sorted view: place the points inside the grid
|
|
60
|
-
order = grid.argsort(
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
order = grid.argsort(A, gridshape=(100, 100, 100))
|
|
67
|
+
Bflat = A[order]
|
|
68
|
+
Bgrid = Bflat.reshape(100, 100, 100, 3)
|
|
63
69
|
|
|
64
70
|
# Rest of your pipeline, working with grids
|
|
65
|
-
|
|
71
|
+
Cgrid = apply_something(Bgrid)
|
|
66
72
|
|
|
67
73
|
# Back to the original points indexing
|
|
68
|
-
|
|
74
|
+
Cflat = Cgrid.reshape(-1, 3)
|
|
69
75
|
orderinv = grid.invert_permutation(order)
|
|
70
|
-
|
|
76
|
+
C = Cflat[orderinv] # matches the initial points order
|
|
71
77
|
```
|
|
72
78
|
|
|
73
79
|
<img src="https://raw.githubusercontent.com/Neighborhood-Grid/CubeNet/main/ballexemple.png">
|
|
@@ -85,6 +91,6 @@ As an example, empirical results in 2-D show that `R = 5` is enough for ~99 % of
|
|
|
85
91
|
The typical use-case of `Gridpoints` is to allow fast local operations on arbitrary point clouds using stencil kernels:
|
|
86
92
|
|
|
87
93
|
```text
|
|
88
|
-
output(i, j, k) = f(
|
|
94
|
+
output(i, j, k) = f( Pgrid[i±di, j±dj, k±dk] | di, dj, dk in local window )
|
|
89
95
|
```
|
|
90
96
|
To go beyond standard (slow) python loops, this can be accelerated with native grid convolution operations of standard libraries whenever possible, or with `pystencils` or `taichi` compilers for complex/non linear grid kernels.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|