foscat 2026.2.2__tar.gz → 2026.2.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.
Files changed (51) hide show
  1. {foscat-2026.2.2/src/foscat.egg-info → foscat-2026.2.5}/PKG-INFO +1 -1
  2. {foscat-2026.2.2 → foscat-2026.2.5}/pyproject.toml +1 -1
  3. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/FoCUS.py +2 -2
  4. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/SphereDownGeo.py +109 -2
  5. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/scat_cov.py +4 -2
  6. {foscat-2026.2.2 → foscat-2026.2.5/src/foscat.egg-info}/PKG-INFO +1 -1
  7. {foscat-2026.2.2 → foscat-2026.2.5}/LICENSE +0 -0
  8. {foscat-2026.2.2 → foscat-2026.2.5}/README.md +0 -0
  9. {foscat-2026.2.2 → foscat-2026.2.5}/setup.cfg +0 -0
  10. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/BkBase.py +0 -0
  11. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/BkNumpy.py +0 -0
  12. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/BkTensorflow.py +0 -0
  13. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/BkTorch.py +0 -0
  14. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/CNN.py +0 -0
  15. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/CircSpline.py +0 -0
  16. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/GCNN.py +0 -0
  17. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/HOrientedConvol.py +0 -0
  18. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/HealBili.py +0 -0
  19. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/HealSpline.py +0 -0
  20. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/Plot.py +0 -0
  21. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/Softmax.py +0 -0
  22. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/SphereUpGeo.py +0 -0
  23. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/SphericalStencil.py +0 -0
  24. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/Spline1D.py +0 -0
  25. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/Synthesis.py +0 -0
  26. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/UNET.py +0 -0
  27. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/__init__.py +0 -0
  28. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/alm.py +0 -0
  29. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/alm_loc.py +0 -0
  30. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/backend.py +0 -0
  31. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/backend_tens.py +0 -0
  32. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/heal_NN.py +0 -0
  33. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/healpix_unet_torch.py +0 -0
  34. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/healpix_vit_skip.py +0 -0
  35. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/healpix_vit_torch-old.py +0 -0
  36. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/healpix_vit_torch.py +0 -0
  37. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/loss_backend_tens.py +0 -0
  38. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/loss_backend_torch.py +0 -0
  39. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/planar_vit.py +0 -0
  40. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/scat.py +0 -0
  41. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/scat1D.py +0 -0
  42. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/scat2D.py +0 -0
  43. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/scat_cov1D.py +0 -0
  44. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/scat_cov2D.py +0 -0
  45. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/scat_cov_map.py +0 -0
  46. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/scat_cov_map2D.py +0 -0
  47. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat/unet_2_d_from_healpix_params.py +0 -0
  48. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat.egg-info/SOURCES.txt +0 -0
  49. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat.egg-info/dependency_links.txt +0 -0
  50. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat.egg-info/requires.txt +0 -0
  51. {foscat-2026.2.2 → foscat-2026.2.5}/src/foscat.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: foscat
3
- Version: 2026.2.2
3
+ Version: 2026.2.5
4
4
  Summary: Generate synthetic Healpix or 2D data using Cross Scattering Transform
5
5
  Author-email: Jean-Marc DELOUIS <jean.marc.delouis@ifremer.fr>
6
6
  Maintainer-email: Theo Foulquier <theo.foulquier@ifremer.fr>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "foscat"
3
- version = "2026.02.2"
3
+ version = "2026.02.5"
4
4
  description = "Generate synthetic Healpix or 2D data using Cross Scattering Transform"
5
5
  readme = "README.md"
6
6
  license = { text = "BSD-3-Clause" }
@@ -39,7 +39,7 @@ class FoCUS:
39
39
  mpi_rank=0
40
40
  ):
41
41
 
42
- self.__version__ = "2026.02.2"
42
+ self.__version__ = "2026.02.4"
43
43
  # P00 coeff for normalization for scat_cov
44
44
  self.TMPFILE_VERSION = TMPFILE_VERSION
45
45
  self.P1_dic = None
@@ -1579,7 +1579,7 @@ class FoCUS:
1579
1579
 
1580
1580
  xx=np.tile(np.arange(self.KERNELSZ)-self.KERNELSZ//2,self.KERNELSZ).reshape(self.KERNELSZ,self.KERNELSZ)
1581
1581
 
1582
- if nside>1:
1582
+ if nside>0:
1583
1583
  wwr=(np.exp(-pw2*(xx**2+(xx.T)**2))*np.cos(pw*xx*np.pi)).reshape(1,1,self.KERNELSZ*self.KERNELSZ)
1584
1584
  wwr-=wwr.mean()
1585
1585
  wwi=(np.exp(-pw2*(xx**2+(xx.T)**2))*np.sin(pw*xx*np.pi)).reshape(1,1,self.KERNELSZ*self.KERNELSZ)
@@ -106,7 +106,7 @@ class SphereDownGeo(nn.Module):
106
106
 
107
107
  M = self._build_down_matrix() # shape (K_out, K_in or N_in)
108
108
 
109
- self.M = M.coalesce()
109
+ self.M = M
110
110
 
111
111
  if use_csr:
112
112
  self.M = self.M.to_sparse_csr().to(self.device)
@@ -209,6 +209,113 @@ class SphereDownGeo(nn.Module):
209
209
  return np.ones_like(w) / max(np.sqrt(w.size), 1.0)
210
210
  return w / np.sqrt(s2)
211
211
 
212
+ def _build_down_matrix(self) -> torch.Tensor:
213
+ nside_in = self.nside_in
214
+ nside_out = self.nside_out
215
+ sigma = float(self.sigma_rad)
216
+
217
+ p_out = self.cell_ids_out.astype(np.int64) # [K]
218
+ K = p_out.size
219
+ offs = np.arange(4, dtype=np.int64) # [4]
220
+
221
+ # --- (A) Choix du voisinage côté coarse
222
+ # Option 1 (minimal, très rapide) : uniquement le parent -> 4 enfants
223
+ #parents = p_out[:, None] # [K,1]
224
+
225
+ # Option 2 (plus “lisse”) : parent + 8 voisins -> 9 parents -> 36 enfants
226
+ neigh8 = hp.get_all_neighbours(nside_out, p_out, nest=True) # [8,K] (healpy renvoie souvent [8,K])
227
+ parents = np.concatenate([p_out[None, :], neigh8], axis=0).T # [K,9]
228
+ idx=np.where(parents==-1)
229
+ parents[idx[0],idx[1]]=parents[idx[0],idx[1]-1]
230
+
231
+ # --- enfants fins (NESTED) : child_id = 4*parent + {0,1,2,3}
232
+ children = (4 * parents[..., None] + offs[None, None, :]).reshape(K, -1) # [K, 4] ou [K,36]
233
+
234
+ # Si option voisins activée : invalider enfants des parents=-1
235
+ #mask_child = np.repeat(mask_parent, 4, axis=1) # [K,36]
236
+ #children_flat = children[mask_child]
237
+ #print(mask_child.shape,children.shape,K)
238
+ rows_flat = np.repeat(np.arange(K, dtype=np.int64), children.shape[1])#[mask_child.ravel()]
239
+
240
+ # Option minimal (sans voisins) :
241
+ children_flat = children.reshape(-1) # [K*4]
242
+ #rows_flat = np.repeat(np.arange(K, dtype=np.int64), children.shape[1])
243
+
244
+ # --- Subset: map vers indices compacts
245
+ if self.has_in_subset:
246
+ in_ids = self.in_cell_ids # trié/unique :contentReference[oaicite:4]{index=4}
247
+ idx = np.searchsorted(in_ids, children_flat)
248
+
249
+ in_range = idx < in_ids.size # idx est toujours >=0 pour searchsorted
250
+ idx2 = idx[in_range]
251
+ child2 = children_flat[in_range]
252
+
253
+ ok2 = (in_ids[idx2] == child2) # comparaison safe
254
+ ok = np.zeros_like(in_range, dtype=bool)
255
+ ok[in_range] = ok2
256
+
257
+ cols_flat = idx[ok]
258
+ rows_flat2 = rows_flat[ok]
259
+ child_ids_kept = children_flat[ok]
260
+ else:
261
+ cols_flat = children_flat
262
+ rows_flat2 = rows_flat
263
+ child_ids_kept = children_flat
264
+
265
+ if rows_flat2.size == 0:
266
+ indices = torch.zeros((2, 0), dtype=torch.long, device=self.device)
267
+ vals_t = torch.zeros((0,), dtype=self.dtype, device=self.device)
268
+ return torch.sparse_coo_tensor(indices, vals_t, size=(self.K_out, self.K_in),
269
+ device=self.device, dtype=self.dtype).coalesce()
270
+
271
+ # --- poids gaussiens (vectorisé)
272
+ # centres coarse: vec0 [K,3]
273
+ vx0, vy0, vz0 = hp.pix2vec(nside_out, p_out, nest=True)
274
+ vec0 = np.stack([vx0, vy0, vz0], axis=1) # [K,3]
275
+
276
+ # vec des enfants gardés
277
+ vx, vy, vz = hp.pix2vec(nside_in, child_ids_kept, nest=True)
278
+ vec = np.stack([vx, vy, vz], axis=1) # [nnz,3]
279
+
280
+ # dot(vec, vec0[row])
281
+ dots = np.einsum("ij,ij->i", vec, vec0[rows_flat2])
282
+ dots = np.clip(dots, -1.0, 1.0)
283
+ ang = np.arccos(dots)
284
+ w = np.exp(-2.0 * (ang / max(sigma, 1e-30)) ** 2)
285
+
286
+ # --- normalisation par ligne (row-wise), sans boucle
287
+ # On recompose un tableau dense “temporaire” [K, m] via indexation
288
+ m = children.shape[1]
289
+ # position within row (0..m-1)
290
+ jpos = np.tile(np.arange(m, dtype=np.int64), K)
291
+ if self.has_in_subset:
292
+ jpos = jpos.reshape(-1)[ok] # aligné sur rows_flat2/child_ids_kept
293
+ # si option voisins + mask_child : jpos = jpos[mask_child.reshape(-1)][ok] etc.
294
+
295
+ W = np.zeros((K, m), dtype=np.float64)
296
+ W[rows_flat2, jpos] = w
297
+
298
+ if self.weight_norm == "l1":
299
+ s = W.sum(axis=1, keepdims=True)
300
+ s[s <= 0] = 1.0
301
+ W /= s
302
+ else: # l2
303
+ s2 = np.sqrt((W * W).sum(axis=1, keepdims=True))
304
+ s2[s2 <= 0] = 1.0
305
+ W /= s2
306
+
307
+ # extraire w normalisés aux mêmes nnz
308
+ w_norm = W[rows_flat2, jpos].astype(np.float32)
309
+
310
+ # --- sparse
311
+ rows_t = torch.tensor(rows_flat2, dtype=torch.long, device=self.device)
312
+ cols_t = torch.tensor(cols_flat, dtype=torch.long, device=self.device)
313
+ vals_t = torch.tensor(w_norm, dtype=self.dtype, device=self.device)
314
+
315
+ indices = torch.stack([rows_t, cols_t], dim=0)
316
+ return torch.sparse_coo_tensor(indices, vals_t, size=(self.K_out, self.K_in),
317
+ device=self.device, dtype=self.dtype).coalesce()
318
+ '''
212
319
  def _build_down_matrix(self) -> torch.Tensor:
213
320
  """Construct sparse matrix M (K_out, K_in or N_in) for the selected coarse pixels."""
214
321
  nside_in = self.nside_in
@@ -302,7 +409,7 @@ class SphereDownGeo(nn.Module):
302
409
  dtype=self.dtype,
303
410
  ).coalesce()
304
411
  return M
305
-
412
+ '''
306
413
  # ---------------- forward ----------------
307
414
  def forward(self, x: torch.Tensor):
308
415
  """
@@ -2378,7 +2378,7 @@ class funct(FOC.FoCUS):
2378
2378
  if image2 is not None:
2379
2379
  tmpi2 = self.up_grade(tmpi2, l_nside * 2)
2380
2380
  l_nside = int(np.sqrt(tmp.shape[-1] // 12))
2381
- nscale = int(np.log(l_nside) / np.log(2))
2381
+ nscale = int(np.log(l_nside) / np.log(2)+1)
2382
2382
  cmat = {}
2383
2383
  cmat2 = {}
2384
2384
 
@@ -2564,7 +2564,7 @@ class funct(FOC.FoCUS):
2564
2564
  mat2[0 : k + 1, None, ...].astype("complex64")
2565
2565
  )
2566
2566
 
2567
- if k < l_nside - 1:
2567
+ if k < nscale - 1:
2568
2568
  tmp, _ = self.ud_grade_2(tmp)
2569
2569
  if image2 is not None:
2570
2570
  tmpi2, _ = self.ud_grade_2(tmpi)
@@ -2702,6 +2702,7 @@ class funct(FOC.FoCUS):
2702
2702
  nside = int(np.sqrt(npix // 12))
2703
2703
 
2704
2704
  J = int(np.log2(nside)+1) # Number of j scales
2705
+
2705
2706
  if cell_ids is not None:
2706
2707
  J=np.min([J,int(np.log(cell_ids.shape[0]) / (2*np.log(2)))-1])
2707
2708
 
@@ -2764,6 +2765,7 @@ class funct(FOC.FoCUS):
2764
2765
  I2 = self.up_grade(I2, nside * 2)
2765
2766
 
2766
2767
  nside = nside * 2
2768
+ Jmax = Jmax +1
2767
2769
 
2768
2770
  # Normalize the masks because they have different pixel numbers
2769
2771
  # vmask /= self.backend.bk_reduce_sum(vmask, axis=1)[:, None] # [Nmask, Npix]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: foscat
3
- Version: 2026.2.2
3
+ Version: 2026.2.5
4
4
  Summary: Generate synthetic Healpix or 2D data using Cross Scattering Transform
5
5
  Author-email: Jean-Marc DELOUIS <jean.marc.delouis@ifremer.fr>
6
6
  Maintainer-email: Theo Foulquier <theo.foulquier@ifremer.fr>
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