squarenet 1.3.0__tar.gz → 1.3.2__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.
- {squarenet-1.3.0 → squarenet-1.3.2}/PKG-INFO +1 -1
- {squarenet-1.3.0 → squarenet-1.3.2}/pyproject.toml +1 -1
- {squarenet-1.3.0 → squarenet-1.3.2}/src/squarenet/__init__.py +1 -1
- {squarenet-1.3.0 → squarenet-1.3.2}/src/squarenet/neighbormap.py +7 -5
- {squarenet-1.3.0 → squarenet-1.3.2}/src/squarenet/squarenet.py +53 -18
- {squarenet-1.3.0 → squarenet-1.3.2}/src/squarenet/utils.py +59 -31
- {squarenet-1.3.0 → squarenet-1.3.2}/src/squarenet.egg-info/PKG-INFO +1 -1
- {squarenet-1.3.0 → squarenet-1.3.2}/README.md +0 -0
- {squarenet-1.3.0 → squarenet-1.3.2}/setup.cfg +0 -0
- {squarenet-1.3.0 → squarenet-1.3.2}/src/squarenet/artist.py +0 -0
- {squarenet-1.3.0 → squarenet-1.3.2}/src/squarenet/core.py +0 -0
- {squarenet-1.3.0 → squarenet-1.3.2}/src/squarenet/data/__init__.py +0 -0
- {squarenet-1.3.0 → squarenet-1.3.2}/src/squarenet/optim/core_numpy.py +0 -0
- {squarenet-1.3.0 → squarenet-1.3.2}/src/squarenet/optim/core_torch.py +0 -0
- {squarenet-1.3.0 → squarenet-1.3.2}/src/squarenet/optim/numpy/booster.py +0 -0
- {squarenet-1.3.0 → squarenet-1.3.2}/src/squarenet/optim/numpy/hashtable.py +0 -0
- {squarenet-1.3.0 → squarenet-1.3.2}/src/squarenet/optim/numpy/subgrid.py +0 -0
- {squarenet-1.3.0 → squarenet-1.3.2}/src/squarenet/optim/torch/booster.py +0 -0
- {squarenet-1.3.0 → squarenet-1.3.2}/src/squarenet/optim/torch/hashtable.py +0 -0
- {squarenet-1.3.0 → squarenet-1.3.2}/src/squarenet/optim/torch/subgrid.py +0 -0
- {squarenet-1.3.0 → squarenet-1.3.2}/src/squarenet/sampler.py +0 -0
- {squarenet-1.3.0 → squarenet-1.3.2}/src/squarenet/views.py +0 -0
- {squarenet-1.3.0 → squarenet-1.3.2}/src/squarenet.egg-info/SOURCES.txt +0 -0
- {squarenet-1.3.0 → squarenet-1.3.2}/src/squarenet.egg-info/dependency_links.txt +0 -0
- {squarenet-1.3.0 → squarenet-1.3.2}/src/squarenet.egg-info/requires.txt +0 -0
- {squarenet-1.3.0 → squarenet-1.3.2}/src/squarenet.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: squarenet
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.2
|
|
4
4
|
Summary: Gridification for point clouds in any dimension (fast greedy optimal transport through Cartesian Sort).
|
|
5
5
|
Author-email: ArmanddeCacqueray <armanddecacqueray@sfr.fr>
|
|
6
6
|
Project-URL: Homepage, https://github.com/ArmanddeCacqueray/SquareNet
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "squarenet"
|
|
7
|
-
version = "1.3.
|
|
7
|
+
version = "1.3.2"
|
|
8
8
|
description = "Gridification for point clouds in any dimension (fast greedy optimal transport through Cartesian Sort)."
|
|
9
9
|
authors = [{ name = "ArmanddeCacqueray", email = "armanddecacqueray@sfr.fr" }]
|
|
10
10
|
readme = "README.md"
|
|
@@ -14,14 +14,19 @@ def neighbormap(grided_points, mapidx, criterion = "rank", thresholdcut = 1,
|
|
|
14
14
|
d = len(gpts.shape) - 1
|
|
15
15
|
wr = windowradius
|
|
16
16
|
ws = 2*wr+1
|
|
17
|
+
N = np.prod(gpts.shape[:-1])
|
|
18
|
+
D = gpts.shape[-1]
|
|
17
19
|
|
|
18
20
|
selection = None
|
|
19
21
|
|
|
20
|
-
if
|
|
21
|
-
N = np.prod(gpts.shape[:-1])
|
|
22
|
+
if N*D*(ws**2) >= sample_size:
|
|
22
23
|
sample_size_x = min(sample_size//(ws**2), N)
|
|
23
24
|
selection = np.random.choice(N, replace = False, size = sample_size_x)
|
|
24
25
|
selection = mapidx(selection)
|
|
26
|
+
else:
|
|
27
|
+
selection = mapidx(np.arange(N))
|
|
28
|
+
mask = np.isfinite(grided_points[selection]).all(axis=-1)
|
|
29
|
+
selection = tuple(dim_coords[mask] for dim_coords in selection)
|
|
25
30
|
|
|
26
31
|
pad_width = [(0, 0)]*(d+1)
|
|
27
32
|
for gax in projection:
|
|
@@ -39,9 +44,6 @@ def neighbormap(grided_points, mapidx, criterion = "rank", thresholdcut = 1,
|
|
|
39
44
|
axis=projection
|
|
40
45
|
)
|
|
41
46
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
47
|
dists = kernel(gpts[selection], gview[selection])
|
|
46
48
|
|
|
47
49
|
if criterion =="value":
|
|
@@ -58,7 +58,8 @@ class SquareNet:
|
|
|
58
58
|
self.max_iter = max_iter
|
|
59
59
|
self.verbose = verbose
|
|
60
60
|
self.backend = backend
|
|
61
|
-
self.
|
|
61
|
+
self.device = "cpu"
|
|
62
|
+
self._torchdevice = "cpu"
|
|
62
63
|
|
|
63
64
|
if backend not in {"numpy", "jax", "torch"}:
|
|
64
65
|
raise ValueError(f"Unknown backend '{backend}' Should be 'numpy' 'torch' or 'jax'")
|
|
@@ -67,14 +68,29 @@ class SquareNet:
|
|
|
67
68
|
self.xp = np
|
|
68
69
|
|
|
69
70
|
elif backend == "jax":
|
|
71
|
+
import jax
|
|
70
72
|
import jax.numpy as jnp
|
|
73
|
+
import shutil
|
|
71
74
|
self.xp = jnp
|
|
75
|
+
self.device = jax.default_backend()
|
|
76
|
+
if shutil.which("nvidia-smi") is not None:
|
|
77
|
+
try:
|
|
78
|
+
import torch
|
|
79
|
+
if torch.cuda.is_available():
|
|
80
|
+
self._torchdevice = torch.device("cuda")
|
|
81
|
+
except ImportError:
|
|
82
|
+
if warnings_ == True:
|
|
83
|
+
warn(
|
|
84
|
+
"SquareNet tryed to import pytorch because a GPU was detected."\
|
|
85
|
+
"Pytorch was not found so SquareNet will default to cpu for the fit"
|
|
86
|
+
)
|
|
87
|
+
|
|
72
88
|
|
|
73
89
|
elif backend == "torch":
|
|
74
90
|
import torch
|
|
75
91
|
self.xp = torch
|
|
76
92
|
if torch.cuda.is_available():
|
|
77
|
-
self.
|
|
93
|
+
self.device = torch.device("cuda")
|
|
78
94
|
|
|
79
95
|
self.warnings_ = warnings_
|
|
80
96
|
if stencil is not None:
|
|
@@ -88,7 +104,7 @@ class SquareNet:
|
|
|
88
104
|
# Internal utilities
|
|
89
105
|
# ------------------------------------------------------------------
|
|
90
106
|
def _to_backend(self, x):
|
|
91
|
-
return to_backend(x, backend = self.backend, device = self.
|
|
107
|
+
return to_backend(x, backend = self.backend, device = self.device, warnings_ = self.warnings_)
|
|
92
108
|
|
|
93
109
|
def _reset_state(self):
|
|
94
110
|
"""Reset internal state to initial configuration."""
|
|
@@ -96,7 +112,7 @@ class SquareNet:
|
|
|
96
112
|
self.grid = self.xp.arange(
|
|
97
113
|
self.N,
|
|
98
114
|
dtype=self.xp.int32,
|
|
99
|
-
device=self.
|
|
115
|
+
device=self.device,
|
|
100
116
|
).reshape(self.gridshape)
|
|
101
117
|
|
|
102
118
|
else:
|
|
@@ -118,6 +134,7 @@ class SquareNet:
|
|
|
118
134
|
if isinstance(points, str):
|
|
119
135
|
points = samplepoints(method=points, size=(self.N, self.D))
|
|
120
136
|
|
|
137
|
+
points = points.clone() if hasattr(points, "clone") else points.copy()
|
|
121
138
|
points = self._to_backend(points)
|
|
122
139
|
N, D = points.shape
|
|
123
140
|
|
|
@@ -133,7 +150,7 @@ class SquareNet:
|
|
|
133
150
|
|
|
134
151
|
return points
|
|
135
152
|
|
|
136
|
-
def _make_stencil(self, n_target, dtype=float, max_iter=
|
|
153
|
+
def _make_stencil(self, n_target, dtype=float, max_iter=100):
|
|
137
154
|
"""Create a grid stencil based on a p-holder norm boundary. see utils.make_stencil
|
|
138
155
|
stencil allow to fit n_target points in a grid with shape bigger than n_target"""
|
|
139
156
|
stencil = make_stencil(self.gridshape, n_target, dtype, max_iter)
|
|
@@ -164,6 +181,7 @@ class SquareNet:
|
|
|
164
181
|
Padding is performed such that points in the middle of the grid are true points
|
|
165
182
|
and points in the corner are fictive points with +- inf coordinates, using the stencil.
|
|
166
183
|
"""
|
|
184
|
+
n_target = len(points)
|
|
167
185
|
if self.stencil is None:
|
|
168
186
|
if self.verbose + verbose >= 2:
|
|
169
187
|
print(
|
|
@@ -174,8 +192,14 @@ class SquareNet:
|
|
|
174
192
|
" >>> saved_stencil = sn.stencil\n"
|
|
175
193
|
" >>> sn = SquareNet(..., stencil=saved_stencil)\n"
|
|
176
194
|
)
|
|
177
|
-
self._make_stencil(n_target=
|
|
178
|
-
|
|
195
|
+
self._make_stencil(n_target=n_target, dtype=points.dtype)
|
|
196
|
+
elif self.xp.isfinite(self.stencil).sum() != self.D*n_target:
|
|
197
|
+
if self.verbose + verbose >= 2:
|
|
198
|
+
print(
|
|
199
|
+
"\n[SquareNet] Update the stencil to match new number of points...\n"
|
|
200
|
+
)
|
|
201
|
+
self._make_stencil(n_target=n_target, dtype=points.dtype)
|
|
202
|
+
|
|
179
203
|
return fill_in(points, self.stencil, self.xp)
|
|
180
204
|
# ------------------------------------------------------------------
|
|
181
205
|
# Core API
|
|
@@ -204,13 +228,24 @@ class SquareNet:
|
|
|
204
228
|
see ``squarenet.core``
|
|
205
229
|
"""
|
|
206
230
|
old_backend = self.backend
|
|
207
|
-
|
|
208
|
-
|
|
231
|
+
old_device = self.device
|
|
232
|
+
|
|
233
|
+
if self._torchdevice != "cpu":
|
|
234
|
+
#just for the fit, will be updated after
|
|
235
|
+
self.backend = "torch"
|
|
236
|
+
self.device = self._torchdevice
|
|
209
237
|
else:
|
|
210
|
-
fit_with_numpy = True
|
|
211
238
|
#just for the fit, will be updated after
|
|
212
239
|
self.backend = "numpy"
|
|
213
|
-
|
|
240
|
+
self.device = "cpu"
|
|
241
|
+
|
|
242
|
+
true_g = np.array(self.gridshape)
|
|
243
|
+
true_g = true_g[true_g > 1]
|
|
244
|
+
D = len(true_g)
|
|
245
|
+
if D >= 5:
|
|
246
|
+
#method "fast" is as good as the other methods in 5D and more,
|
|
247
|
+
#robust and ultimate are too slow anyway in those cases
|
|
248
|
+
method = "fast"
|
|
214
249
|
max_iter = self.max_iter
|
|
215
250
|
verbose = self.verbose
|
|
216
251
|
def _log(msg):
|
|
@@ -222,7 +257,7 @@ class SquareNet:
|
|
|
222
257
|
print(f"\n=== {title} ===")
|
|
223
258
|
|
|
224
259
|
points = self._validate_points(points)
|
|
225
|
-
|
|
260
|
+
grid = self._to_backend(self.grid)
|
|
226
261
|
|
|
227
262
|
_log("Starting gridification... available method [fast, robust, ultimate]")
|
|
228
263
|
_log(f"selected {method}")
|
|
@@ -236,8 +271,8 @@ class SquareNet:
|
|
|
236
271
|
if method == "ultimate":
|
|
237
272
|
_log(f"(max iter: 2 x {mi} + 4 x {mi} + {mi})")
|
|
238
273
|
|
|
239
|
-
|
|
240
|
-
|
|
274
|
+
grid, lc = carthesian_sort(grid, points, max_iter=max_iter, method = method,
|
|
275
|
+
backend = self.backend, verbose = self.verbose)
|
|
241
276
|
|
|
242
277
|
# --------------------------------------------------
|
|
243
278
|
# Finalization
|
|
@@ -267,10 +302,10 @@ class SquareNet:
|
|
|
267
302
|
stacklevel=2,
|
|
268
303
|
)
|
|
269
304
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
305
|
+
self.backend = old_backend
|
|
306
|
+
self.device = old_device
|
|
307
|
+
self.grid = self._to_backend(grid)
|
|
308
|
+
self.points = self._to_backend(points)
|
|
274
309
|
# --------------------------------------------------
|
|
275
310
|
# Update mappings
|
|
276
311
|
# --------------------------------------------------
|
|
@@ -8,7 +8,7 @@ def index_identity(shape):
|
|
|
8
8
|
"""index_identy[i, j, k, ...] = [i, j, k, ...]"""
|
|
9
9
|
return np.moveaxis(np.indices(shape), 0, -1)
|
|
10
10
|
|
|
11
|
-
def make_stencil(gridshape, n_target, dtype=float, max_iter =
|
|
11
|
+
def make_stencil(gridshape, n_target, dtype=float, max_iter = 100):
|
|
12
12
|
"""
|
|
13
13
|
Create a grid stencil based on a p-holder norm boundary.
|
|
14
14
|
Uses binary search to find a smooth convexe subdomain of the hyperrectangular
|
|
@@ -52,7 +52,7 @@ def make_stencil(gridshape, n_target, dtype=float, max_iter = 1000):
|
|
|
52
52
|
|
|
53
53
|
log_x = np.log(x_abs)
|
|
54
54
|
|
|
55
|
-
low_p, high_p =
|
|
55
|
+
low_p, high_p = 2.0, 1000.0
|
|
56
56
|
|
|
57
57
|
for it in range(max_iter):
|
|
58
58
|
p = (low_p + high_p) / 2.0
|
|
@@ -216,50 +216,78 @@ def project(gridpoints, feature_axes=(0, 1), index=0):
|
|
|
216
216
|
|
|
217
217
|
def from_backend(x):
|
|
218
218
|
"""
|
|
219
|
-
|
|
219
|
+
Safely convert Torch, JAX, or array-like objects back to Numpy.
|
|
220
220
|
"""
|
|
221
|
+
# 1. Already a Numpy array
|
|
221
222
|
if isinstance(x, np.ndarray):
|
|
222
223
|
return x
|
|
223
224
|
|
|
224
|
-
#
|
|
225
|
+
# 2. PyTorch tensor
|
|
225
226
|
if hasattr(x, "detach"):
|
|
226
227
|
return x.detach().cpu().numpy()
|
|
227
228
|
|
|
228
|
-
#
|
|
229
|
-
if hasattr(x, "__array__"):
|
|
230
|
-
return np.asarray(x)
|
|
231
|
-
|
|
229
|
+
# 3. JAX arrays or any object with __array__ protocol (Pandas, etc.)
|
|
232
230
|
return np.asarray(x)
|
|
233
231
|
|
|
234
|
-
def to_backend(x, backend="numpy", device
|
|
232
|
+
def to_backend(x, backend="numpy", device="cpu", warnings_=True):
|
|
235
233
|
"""
|
|
236
|
-
Convert
|
|
234
|
+
Convert arrays/tensors between Numpy, Torch, and JAX.
|
|
235
|
+
Utilizes DLPack for zero-copy transfers across frameworks when possible.
|
|
237
236
|
"""
|
|
237
|
+
# Identify input framework without heavy imports
|
|
238
|
+
in_type = type(x).__module__.split('.')[0]
|
|
239
|
+
|
|
240
|
+
# ---------------------------------------------------------
|
|
241
|
+
# 1. Target: NUMPY
|
|
242
|
+
# ---------------------------------------------------------
|
|
238
243
|
if backend == "numpy":
|
|
239
|
-
return
|
|
244
|
+
return from_backend(x)
|
|
240
245
|
|
|
241
|
-
|
|
246
|
+
# ---------------------------------------------------------
|
|
247
|
+
# 2. Target: TORCH
|
|
248
|
+
# ---------------------------------------------------------
|
|
249
|
+
elif backend == "torch":
|
|
242
250
|
import torch
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
if
|
|
246
|
-
warnings_
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
251
|
+
tgt_dev = torch.device(device)
|
|
252
|
+
|
|
253
|
+
if in_type == "torch":
|
|
254
|
+
if warnings_ and x.device.type != "cpu" and tgt_dev.type == "cpu":
|
|
255
|
+
warn("Downgrading tensor: initial device was GPU but asked for CPU.")
|
|
256
|
+
return x.to(tgt_dev)
|
|
257
|
+
|
|
258
|
+
elif in_type in ["jax", "jaxlib"]:
|
|
259
|
+
# Zero-copy JAX to Torch via DLPack
|
|
260
|
+
return torch.from_dlpack(x).to(tgt_dev)
|
|
261
|
+
|
|
262
|
+
return torch.as_tensor(x, device=tgt_dev)
|
|
263
|
+
|
|
264
|
+
# ---------------------------------------------------------
|
|
265
|
+
# 3. Target: JAX
|
|
266
|
+
# ---------------------------------------------------------
|
|
267
|
+
elif backend == "jax":
|
|
268
|
+
import jax
|
|
259
269
|
import jax.numpy as jnp
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
270
|
+
|
|
271
|
+
# JAX uses "gpu" instead of "cuda"
|
|
272
|
+
device = "cpu" if "cpu" in str(device) else "gpu"
|
|
273
|
+
try:
|
|
274
|
+
tgt_dev = jax.devices(device)[0]
|
|
275
|
+
except RuntimeError:
|
|
276
|
+
tgt_dev = None
|
|
277
|
+
|
|
278
|
+
if in_type == "torch":
|
|
279
|
+
import torch.utils.dlpack
|
|
280
|
+
# DLPack requires contiguous memory
|
|
281
|
+
x = x if x.is_contiguous() else x.contiguous()
|
|
282
|
+
j_arr = jax.dlpack.from_dlpack(torch.utils.dlpack.to_dlpack(x))
|
|
283
|
+
return jax.device_put(j_arr, tgt_dev) if tgt_dev else j_arr
|
|
284
|
+
|
|
285
|
+
elif in_type in ["jax", "jaxlib"]:
|
|
286
|
+
return jax.device_put(x, tgt_dev) if tgt_dev else x
|
|
287
|
+
|
|
288
|
+
return jax.device_put(x, tgt_dev) if tgt_dev else jnp.asarray(x)
|
|
289
|
+
|
|
290
|
+
raise ValueError(f"Unknown backend: '{backend}'. Expected 'numpy', 'torch', or 'jax'.")
|
|
263
291
|
|
|
264
292
|
def progress_bar(it, total, bar_length=30):
|
|
265
293
|
progress = it / total
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: squarenet
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.2
|
|
4
4
|
Summary: Gridification for point clouds in any dimension (fast greedy optimal transport through Cartesian Sort).
|
|
5
5
|
Author-email: ArmanddeCacqueray <armanddecacqueray@sfr.fr>
|
|
6
6
|
Project-URL: Homepage, https://github.com/ArmanddeCacqueray/SquareNet
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|