gridpoints 1.0.2__tar.gz → 1.0.4__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gridpoints
3
- Version: 1.0.2
3
+ Version: 1.0.4
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 faster sorting algorithm.
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 `X(N, D)`
22
- → find a grid shape and a permutation `order` such that
23
- Xgrid = X[order].reshape(*gridshape, D) is sorted along every axis of the grid.
24
- → On the `Xgrid` view of `X`, neighbor queries become a simple stencil look-up
25
- neighborhood[i, j, k] = {Xgrid[i±di, j±dj, k±dk] | (di, dj, dk) ≤ R}, where `R` is a radius cutoff to determine,
26
- allowing local operations in linear time.
27
- → Standard operations (grid convolution, clustering, …) can then be applied
28
- directly on the `Xgrid` view instead of relying on complex graph convolutions
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
- `X` can be a NumPy, PyTorch or CuPy array of any dimension (N, D).
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
- This allow to deal with prime or variable N, as long as one is ready to
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
- X = np.random.rand(1_000_000, 3)
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(X, gridshape=(100, 100, 100))
61
- Yflat = X[order]
62
- Ygrid = Yflat.reshape(100, 100, 100, 3)
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
- Zgrid = apply_something(Ygrid)
71
+ Cgrid = apply_something(Bgrid)
66
72
 
67
73
  # Back to the original points indexing
68
- Zflat = Zgrid.reshape(-1, 2)
74
+ Cflat = Cgrid.reshape(-1, 3)
69
75
  orderinv = grid.invert_permutation(order)
70
- Z = Zflat[orderinv] # matches the initial points order
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( Xgrid[i±di, j±dj, k±dk] | di, dj, dk in local window )
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 faster sorting algorithm.
4
-
5
- Take raw point cloud `X(N, D)`
6
- → find a grid shape and a permutation `order` such that
7
- Xgrid = X[order].reshape(*gridshape, D) is sorted along every axis of the grid.
8
- → On the `Xgrid` view of `X`, neighbor queries become a simple stencil look-up
9
- neighborhood[i, j, k] = {Xgrid[i±di, j±dj, k±dk] | (di, dj, dk) ≤ R}, where `R` is a radius cutoff to determine,
10
- allowing local operations in linear time.
11
- → Standard operations (grid convolution, clustering, …) can then be applied
12
- directly on the `Xgrid` view instead of relying on complex graph convolutions
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
- `X` can be a NumPy, PyTorch or CuPy array of any dimension (N, D).
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
- This allow to deal with prime or variable N, as long as one is ready to
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
- X = np.random.rand(1_000_000, 3)
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(X, gridshape=(100, 100, 100))
45
- Yflat = X[order]
46
- Ygrid = Yflat.reshape(100, 100, 100, 3)
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
- Zgrid = apply_something(Ygrid)
55
+ Cgrid = apply_something(Bgrid)
50
56
 
51
57
  # Back to the original points indexing
52
- Zflat = Zgrid.reshape(-1, 2)
58
+ Cflat = Cgrid.reshape(-1, 3)
53
59
  orderinv = grid.invert_permutation(order)
54
- Z = Zflat[orderinv] # matches the initial points order
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( Xgrid[i±di, j±dj, k±dk] | di, dj, dk in local window )
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.2"
7
+ version = "1.0.4"
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"
@@ -7,4 +7,4 @@ __all__ = (
7
7
  print_zoom,
8
8
  )
9
9
 
10
- __version__ = "1.0.2"
10
+ __version__ = "1.0.4"
@@ -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-len, i+side+1):
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-len, j+side+1):
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-len, k+side+1):
119
+ for kk in range(k-side, k+side+1):
120
120
  print(_format_point(Ygrid[i, j, kk]), end=" ")
121
121
  print()
@@ -8,4 +8,4 @@ __all__ = (
8
8
  "invert_permutation",
9
9
  )
10
10
 
11
- __version__ = "1.0.2"
11
+ __version__ = "1.0.4"
@@ -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):
@@ -47,8 +47,7 @@ def inplace_sort(X, axis):
47
47
  if backend_name == 'torch':
48
48
  X.copy_(X.sort(dim=axis)[0])
49
49
  if backend_name == "cupy":
50
- X[...] = xp.ascontiguousarray(X)
51
- X.sort(axis=axis)
50
+ X[...] = xp.sort(xp.ascontiguousarray(X), axis=axis)
52
51
  else:
53
52
  X.sort(axis=axis)
54
53
 
@@ -101,8 +100,8 @@ def nan_to_num(arr):
101
100
  finite = xp.isfinite(x)
102
101
  vals = x[finite]
103
102
 
104
- out[xp.isposinf(x), d] = 2 * vals.max()
105
- out[xp.isneginf(x), d] = 2 * vals.min()
103
+ out[xp.isposinf(x), d] = vals.max() + 1
104
+ out[xp.isneginf(x), d] = vals.min() - 1
106
105
 
107
106
  nan = xp.isnan(x)
108
107
  n = nan.sum().item() if backend == 'torch' else int(nan.sum())
@@ -165,4 +164,4 @@ def transpose(X, axes):
165
164
  _, name = get_backend(X)
166
165
  if name == "torch":
167
166
  return X.permute(*axes)
168
- 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=50, verbose = 2):
21
+ def argsort(X, gridshape, level=2, init="kdtree", n_iter=40, verbose = 2):
22
22
  """Compute a multi-directional monotonic sorted ordering of a point cloud.
23
23
 
24
24
  Parameters
@@ -40,7 +40,7 @@ def argsort(X, gridshape, level=2, init="kdtree", n_iter=50, verbose = 2):
40
40
  init : {'kdtree', 'shuffle'} or None, default="kdtree"
41
41
  Initialization method for the permutation order. Try init = None
42
42
  if the point cloud is already nearly sorted.
43
- n_iter : int, default=50
43
+ n_iter : int, default=40
44
44
  Maximum number of optimization iterations.
45
45
  verbose : int, default 2
46
46
  0 = silent, 1 = log progress, 2 = 1 + warn if convergence failed
@@ -50,9 +50,6 @@ def argsort(X, gridshape, level=2, init="kdtree", n_iter=50, verbose = 2):
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, converged
66
+ return order
71
67
  else:
72
68
  order = arange(len(X), 'int', reference_array=X)
73
69
  if init == "shuffle":
@@ -77,7 +73,7 @@ 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
@@ -85,19 +81,19 @@ def argsort(X, gridshape, level=2, init="kdtree", n_iter=50, verbose = 2):
85
81
  order = order[ordernew]
86
82
  if not converged and verbose >=2:
87
83
  warnings.warn(
88
- f"Algorithm failed to converge after {n_iter} iterations. " \
89
- "Consider checking if the grid structure is already suitable" \
90
- "for the given task, which is higly probable, or increase the" \
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" \
91
87
  "`n_iter` parameter if strict convergence is required.",
92
88
  category=ConvergenceWarning,
93
- stacklevel=2,
89
+ stacklevel=2
94
90
  )
95
91
  if verbose:
96
92
  print("done")
97
- return order, converged
93
+ return order
98
94
 
99
95
 
100
- def sort(X, gridshape, level=2, init="kdtree", inplace=False, n_iter=50, verbose = 2):
96
+ def sort(X, gridshape, level=2, init="kdtree", inplace=False, n_iter=40, verbose = 2):
101
97
  """Sort a point cloud `X` using the monotonic Lagrangian algorithm.
102
98
 
103
99
  Parameters
@@ -121,7 +117,7 @@ def sort(X, gridshape, level=2, init="kdtree", inplace=False, n_iter=50, verbose
121
117
  if the point cloud is already nearly sorted.
122
118
  inplace : bool, default=False
123
119
  If True, modify the point cloud `X` in place.
124
- n_iter : int, default=50
120
+ n_iter : int, default=40
125
121
  Maximum number of optimization iterations.
126
122
  verbose : int, default 2
127
123
  0 = silent, 1 = log progress, 2 = 1 + warn if convergence failed
@@ -132,7 +128,7 @@ def sort(X, gridshape, level=2, init="kdtree", inplace=False, n_iter=50, verbose
132
128
  The sorted point cloud such that `X_sorted.reshape(*shape, D)` forms a
133
129
  monotonically sorted grid. Returns `None` if `inplace=True`.
134
130
  """
135
- order, _ = argsort(X, gridshape, level=level, init = init, n_iter=n_iter,verbose = verbose)
131
+ order = argsort(X, gridshape, level=level, init = init, n_iter=n_iter,verbose = verbose)
136
132
 
137
133
  if inplace:
138
134
  X[:] = X[order]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gridpoints
3
- Version: 1.0.2
3
+ Version: 1.0.4
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 faster sorting algorithm.
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 `X(N, D)`
22
- → find a grid shape and a permutation `order` such that
23
- Xgrid = X[order].reshape(*gridshape, D) is sorted along every axis of the grid.
24
- → On the `Xgrid` view of `X`, neighbor queries become a simple stencil look-up
25
- neighborhood[i, j, k] = {Xgrid[i±di, j±dj, k±dk] | (di, dj, dk) ≤ R}, where `R` is a radius cutoff to determine,
26
- allowing local operations in linear time.
27
- → Standard operations (grid convolution, clustering, …) can then be applied
28
- directly on the `Xgrid` view instead of relying on complex graph convolutions
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
- `X` can be a NumPy, PyTorch or CuPy array of any dimension (N, D).
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
- This allow to deal with prime or variable N, as long as one is ready to
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
- X = np.random.rand(1_000_000, 3)
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(X, gridshape=(100, 100, 100))
61
- Yflat = X[order]
62
- Ygrid = Yflat.reshape(100, 100, 100, 3)
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
- Zgrid = apply_something(Ygrid)
71
+ Cgrid = apply_something(Bgrid)
66
72
 
67
73
  # Back to the original points indexing
68
- Zflat = Zgrid.reshape(-1, 2)
74
+ Cflat = Cgrid.reshape(-1, 3)
69
75
  orderinv = grid.invert_permutation(order)
70
- Z = Zflat[orderinv] # matches the initial points order
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( Xgrid[i±di, j±dj, k±dk] | di, dj, dk in local window )
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