foscat 2026.2.3__py3-none-any.whl → 2026.2.5__py3-none-any.whl

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.
foscat/FoCUS.py CHANGED
@@ -39,7 +39,7 @@ class FoCUS:
39
39
  mpi_rank=0
40
40
  ):
41
41
 
42
- self.__version__ = "2026.02.3"
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)
foscat/SphereDownGeo.py CHANGED
@@ -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
  """
foscat/scat_cov.py CHANGED
@@ -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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: foscat
3
- Version: 2026.2.3
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>
@@ -4,14 +4,14 @@ foscat/BkTensorflow.py,sha256=iIdLx6VTOfOEocfZBOGyizQn5geDLTfdWWAwDeQr9YA,20056
4
4
  foscat/BkTorch.py,sha256=9utOTAfS99E7DPN0QfCnn5ULS1WdS4z3-Z0XJMriJY4,53202
5
5
  foscat/CNN.py,sha256=4vky7jqTshL1aYLWsc-hQwf7gDjTVjL7I6HZiAsa6x4,5158
6
6
  foscat/CircSpline.py,sha256=CXi49FxF8ZoeZ17Ua8c1AZXe2B5ICEC9aCXb97atB3s,4028
7
- foscat/FoCUS.py,sha256=8dTRKUNXHHqLDESmsEr-xWXArZWLrmEase2I1y_8BVs,109790
7
+ foscat/FoCUS.py,sha256=pK8cmsUSbaAUJbfoXGpQM9Fo2HCprBFAnMyW4bjRbzM,109790
8
8
  foscat/GCNN.py,sha256=q7yWHCMJpP7-m3WvR3OQnp5taeYWaMxIY2hQ6SIb9gs,4487
9
9
  foscat/HOrientedConvol.py,sha256=xMaS-zzoUyXisBCPsHBVpn54tuA9Qv3na-tT86Cwn7U,38744
10
10
  foscat/HealBili.py,sha256=YRPk9PO5G8NdwKeb33xiJs3_pMPAgIv5phCs8GT6LN0,12943
11
11
  foscat/HealSpline.py,sha256=YRotJ1NQuXYFyFiM8fp6qkATIwRJ8lqIVo4vGXpHO-w,7472
12
12
  foscat/Plot.py,sha256=bpohWGsblTBxMrqE_X-iRvuvT-YyHDcgfWB4iYk5l10,49218
13
13
  foscat/Softmax.py,sha256=UDZGrTroYtmGEyokGUVpwNO_cgbICi9QVuRr8Yx52_k,2917
14
- foscat/SphereDownGeo.py,sha256=B_ENfL4Mqn6VmpXEGGxEW8DR3R6zCNV-YVtsU00xwjY,15380
14
+ foscat/SphereDownGeo.py,sha256=UprhvwgBNH996L70hksfgZ33S6rcvL9qjz_ZTHFhH6U,20162
15
15
  foscat/SphereUpGeo.py,sha256=KvvXqVRW7eX79MJsJJS6g5ll84Qyx0bLZzQaDXy4fp4,7023
16
16
  foscat/SphericalStencil.py,sha256=kRyQXwZyoxdRW326e2ht8eGzg1-Lvl89tdHSpVTfJzg,47620
17
17
  foscat/Spline1D.py,sha256=rKzzenduaZZ-yBDJd35it6Gyrj1spqb7hoIaUgISPzY,2983
@@ -33,14 +33,14 @@ foscat/planar_vit.py,sha256=lQqwyz_P8G-Dav2vLqgkssDfeSe15YmjFzP5W-otjs0,6888
33
33
  foscat/scat.py,sha256=emN7MsSuoJzPWQfgDETScZeKLmu8K-jjEh5Dp7szQzY,72809
34
34
  foscat/scat1D.py,sha256=FvvxwhlzxwvIETkenpisvDteJ06DBYP5K7wsnJ2nHjM,53713
35
35
  foscat/scat2D.py,sha256=boKj0ASqMMSy7uQLK6hPniG87m3hZGJBYBiq5v8F9IQ,532
36
- foscat/scat_cov.py,sha256=enGg_OdhB1pXvvoG3ECt_9K-sFlwOjzyn_jaoC_Kmes,271818
36
+ foscat/scat_cov.py,sha256=F9TpikUi4njrLmKHsSCtfSUJ6cepYXVXYjXVBzsTbd8,271819
37
37
  foscat/scat_cov1D.py,sha256=XOxsZZ5TYq8f34i2tUgIfzyaqaTDlICB3HzD2l_puro,531
38
38
  foscat/scat_cov2D.py,sha256=pAm0fKw8wyXram0TFbtw8tGcc8QPKuPXpQk0kh10r4U,7078
39
39
  foscat/scat_cov_map.py,sha256=9MzpwT2g9S3dmnjHEMK7PPLQ27oGQg2VFVsP_TDUU5E,2869
40
40
  foscat/scat_cov_map2D.py,sha256=zaIIYshXCqAeZ04I158GhD-Op4aoMlLnLEy7rxckVYY,2842
41
41
  foscat/unet_2_d_from_healpix_params.py,sha256=r8hN-s091f3yHYlvAAiBbLOvtsz9vPrdwrWPM0ULR2Q,15949
42
- foscat-2026.2.3.dist-info/licenses/LICENSE,sha256=i0ukIr8ZUpkSY2sZaE9XZK-6vuSU5iG6IgX_3pjatP8,1505
43
- foscat-2026.2.3.dist-info/METADATA,sha256=DOWjA-o0BmegWKQQV4olIwL5L6FJePFOdLZujE1-YFM,4489
44
- foscat-2026.2.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
45
- foscat-2026.2.3.dist-info/top_level.txt,sha256=AGySXBBAlJgb8Tj8af6m_F-aiNg2zNTcybCUPVOKjAg,7
46
- foscat-2026.2.3.dist-info/RECORD,,
42
+ foscat-2026.2.5.dist-info/licenses/LICENSE,sha256=i0ukIr8ZUpkSY2sZaE9XZK-6vuSU5iG6IgX_3pjatP8,1505
43
+ foscat-2026.2.5.dist-info/METADATA,sha256=U6Edka4THjcirgIpweyeLh0EGdV6JqSHuwND147x6JU,4489
44
+ foscat-2026.2.5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
45
+ foscat-2026.2.5.dist-info/top_level.txt,sha256=AGySXBBAlJgb8Tj8af6m_F-aiNg2zNTcybCUPVOKjAg,7
46
+ foscat-2026.2.5.dist-info/RECORD,,