Equimo 0.4.6a2__tar.gz → 0.4.6a4__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 (45) hide show
  1. {equimo-0.4.6a2/src/Equimo.egg-info → equimo-0.4.6a4}/PKG-INFO +1 -1
  2. {equimo-0.4.6a2 → equimo-0.4.6a4}/pyproject.toml +1 -1
  3. {equimo-0.4.6a2 → equimo-0.4.6a4/src/Equimo.egg-info}/PKG-INFO +1 -1
  4. equimo-0.4.6a4/src/equimo/__init__.py +1 -0
  5. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/layers/convolution.py +60 -26
  6. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/layers/wavelet.py +24 -10
  7. equimo-0.4.6a2/src/equimo/__init__.py +0 -1
  8. {equimo-0.4.6a2 → equimo-0.4.6a4}/LICENSE.md +0 -0
  9. {equimo-0.4.6a2 → equimo-0.4.6a4}/README.md +0 -0
  10. {equimo-0.4.6a2 → equimo-0.4.6a4}/setup.cfg +0 -0
  11. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/Equimo.egg-info/SOURCES.txt +0 -0
  12. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/Equimo.egg-info/dependency_links.txt +0 -0
  13. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/Equimo.egg-info/requires.txt +0 -0
  14. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/Equimo.egg-info/top_level.txt +0 -0
  15. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/conversion/__init__.py +0 -0
  16. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/conversion/utils.py +0 -0
  17. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/experimental/__init__.py +0 -0
  18. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/experimental/text.py +0 -0
  19. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/io.py +0 -0
  20. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/layers/__init__.py +0 -0
  21. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/layers/activation.py +0 -0
  22. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/layers/attention.py +0 -0
  23. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/layers/downsample.py +0 -0
  24. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/layers/dropout.py +0 -0
  25. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/layers/ffn.py +0 -0
  26. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/layers/generic.py +0 -0
  27. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/layers/mamba.py +0 -0
  28. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/layers/norm.py +0 -0
  29. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/layers/patch.py +0 -0
  30. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/layers/posemb.py +0 -0
  31. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/layers/sharing.py +0 -0
  32. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/layers/squeeze_excite.py +0 -0
  33. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/models/__init__.py +0 -0
  34. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/models/emamodel.py +0 -0
  35. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/models/fastervit.py +0 -0
  36. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/models/lowformer.py +0 -0
  37. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/models/mlla.py +0 -0
  38. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/models/partialformer.py +0 -0
  39. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/models/reduceformer.py +0 -0
  40. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/models/shvit.py +0 -0
  41. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/models/vit.py +0 -0
  42. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/models/vssd.py +0 -0
  43. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/ops/scan.py +0 -0
  44. {equimo-0.4.6a2 → equimo-0.4.6a4}/src/equimo/utils.py +0 -0
  45. {equimo-0.4.6a2 → equimo-0.4.6a4}/tests/test_models.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Equimo
3
- Version: 0.4.6a2
3
+ Version: 0.4.6a4
4
4
  Summary: Implementation of popular vision models in Jax
5
5
  Classifier: Development Status :: 4 - Beta
6
6
  Classifier: Intended Audience :: Developers
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "Equimo"
3
- version = "0.4.6a2"
3
+ version = "0.4.6a4"
4
4
  description = "Implementation of popular vision models in Jax"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Equimo
3
- Version: 0.4.6a2
3
+ Version: 0.4.6a4
4
4
  Summary: Implementation of popular vision models in Jax
5
5
  Classifier: Development Status :: 4 - Beta
6
6
  Classifier: Intended Audience :: Developers
@@ -0,0 +1 @@
1
+ __version__ = "0.4.6a4"
@@ -1125,13 +1125,13 @@ class GenericGhostModule(eqx.Module):
1125
1125
  self.mode = mode
1126
1126
  self.num_conv_branches = num_conv_branches
1127
1127
  self.out_channels = out_channels
1128
- self.kernel_size = kernel_size # MOD
1129
- self.dw_size = dw_size # MOD
1130
- self.stride = stride # MOD
1131
- self.primary_has_skip = primary_has_skip # MOD
1132
- self.primary_has_scale = primary_has_scale # MOD
1133
- self.cheap_has_skip = cheap_has_skip # MOD
1134
- self.cheap_has_scale = cheap_has_scale # MOD
1128
+ self.kernel_size = kernel_size
1129
+ self.dw_size = dw_size
1130
+ self.stride = stride
1131
+ self.primary_has_skip = primary_has_skip
1132
+ self.primary_has_scale = primary_has_scale
1133
+ self.cheap_has_skip = cheap_has_skip
1134
+ self.cheap_has_scale = cheap_has_scale
1135
1135
 
1136
1136
  # Those are actually placeholders, updated at each epoch, only used at inference time
1137
1137
  self.primary_conv = eqx.nn.Conv2d(
@@ -1147,7 +1147,7 @@ class GenericGhostModule(eqx.Module):
1147
1147
  in_channels=init_channels,
1148
1148
  out_channels=new_channels,
1149
1149
  kernel_size=dw_size,
1150
- stride=1, # MOD
1150
+ stride=1,
1151
1151
  padding=dw_size // 2,
1152
1152
  groups=init_channels,
1153
1153
  use_bias=False,
@@ -1300,16 +1300,31 @@ class GenericGhostModule(eqx.Module):
1300
1300
  inference: Optional[bool] = None,
1301
1301
  ):
1302
1302
  use_inference = self.inference if inference is None else inference
1303
- out = self.inference_features(x) if use_inference else self.training_features(x)
1303
+ use_inference = jnp.asarray(use_inference, dtype=bool)
1304
+
1305
+ out = jax.lax.cond(
1306
+ use_inference,
1307
+ lambda x_: self.inference_features(x_),
1308
+ lambda x_: self.training_features(x_),
1309
+ operand=x,
1310
+ )
1304
1311
 
1305
- if self.mode == "shortcut":
1306
- res = self.short_conv(self.pool2(x))
1307
- gating_signal = jax.image.resize(
1312
+ def _shortcut_branch(ox):
1313
+ out_, x_ = ox
1314
+ res = self.short_conv(self.pool2(x_))
1315
+ gating = jax.image.resize(
1308
1316
  image=jax.nn.sigmoid(res / self.gate_scale),
1309
- shape=(res.shape[0], out.shape[1], out.shape[2]),
1317
+ shape=(res.shape[0], out_.shape[1], out_.shape[2]),
1310
1318
  method="nearest",
1311
1319
  )
1312
- out = out[: self.out_channels, :, :] * gating_signal
1320
+ return out_[: self.out_channels, :, :] * gating[: self.out_channels, :, :]
1321
+
1322
+ out = jax.lax.cond(
1323
+ self.mode == "shortcut",
1324
+ _shortcut_branch,
1325
+ lambda ox: ox[0],
1326
+ operand=(out, x),
1327
+ )
1313
1328
 
1314
1329
  return out
1315
1330
 
@@ -1347,7 +1362,6 @@ class GhostBottleneck(eqx.Module):
1347
1362
  stride: int = 1,
1348
1363
  act_layer: Callable = jax.nn.relu,
1349
1364
  se_ratio: float = 0.0,
1350
- layer_id: Optional[int] = None,
1351
1365
  use_shortcut_mode_in_ghost1: bool = True,
1352
1366
  allow_identity_residual: bool = True,
1353
1367
  key: PRNGKeyArray,
@@ -1490,34 +1504,54 @@ class GhostBottleneck(eqx.Module):
1490
1504
  inference: Optional[bool] = None,
1491
1505
  ) -> Float[Array, "channels height width"]:
1492
1506
  use_inference = self.inference if inference is None else inference
1493
- k_g1, k_g2 = jr.split(key, 2)
1507
+ use_inference = jax.numpy.asarray(use_inference, dtype=bool)
1494
1508
 
1509
+ k_g1, k_g2 = jr.split(key, 2)
1495
1510
  residual = x
1496
1511
 
1497
1512
  x = self.ghost1(x, key=k_g1, inference=use_inference)
1498
1513
 
1499
1514
  if self.stride > 1:
1500
- if use_inference:
1501
- x = self.dw_shared_norm(self.dw_conv(x))
1502
- else:
1503
- terms = []
1504
- if not isinstance(self.dw_rpr_scale, eqx.nn.Identity):
1505
- terms.append(self.dw_rpr_scale(x))
1506
- terms.extend(conv(x) for conv in self.dw_rpr_conv)
1507
- x = self.dw_shared_norm(jax.tree_util.tree_reduce(operator.add, terms))
1515
+ x = jax.lax.cond(
1516
+ use_inference,
1517
+ lambda y: self.dw_shared_norm(self.dw_conv(y)),
1518
+ lambda y: self.dw_shared_norm(
1519
+ jax.tree_util.tree_reduce(
1520
+ operator.add,
1521
+ (
1522
+ (
1523
+ []
1524
+ if isinstance(self.dw_rpr_scale, eqx.nn.Identity)
1525
+ else [self.dw_rpr_scale(y)]
1526
+ )
1527
+ + [conv(y) for conv in self.dw_rpr_conv]
1528
+ ),
1529
+ )
1530
+ ),
1531
+ operand=x,
1532
+ )
1508
1533
 
1509
1534
  x = self.se(x)
1510
1535
 
1511
1536
  x = self.ghost2(x, key=k_g2, inference=use_inference)
1512
1537
 
1513
1538
  if not isinstance(self.shortcut, eqx.nn.Identity):
1514
- x += self.shortcut(residual)
1539
+ x = x + self.shortcut(residual)
1515
1540
  elif self.allow_identity_residual:
1516
- x += residual
1541
+ x = x + residual
1517
1542
 
1518
1543
  return x
1519
1544
 
1520
1545
 
1546
+ key = jr.PRNGKey(42)
1547
+ x = jr.normal(key, (64, 16, 16))
1548
+ f = GhostBottleneck(64, 128, 64, se_ratio=0.25, key=key)
1549
+ x1 = f.ghost1(x, key=key)
1550
+ x2 = f.se(x1)
1551
+ x3 = f.ghost2(x2, key=key)
1552
+ print(x2.shape)
1553
+
1554
+
1521
1555
  def _pad_kernel_to_target(kernel: Array, target_size: tuple[int, int]) -> Array:
1522
1556
  kh, kw = kernel.shape[-2], kernel.shape[-1]
1523
1557
  th, tw = target_size
@@ -63,7 +63,7 @@ def haar_dwt_split(
63
63
 
64
64
 
65
65
  class HWDConv(eqx.Module):
66
- mode: Literal["h_discard", "accurate"] = eqx.field(static=True)
66
+ mode: Literal["h_discard", "band_grouped", "accurate"] = eqx.field(static=True)
67
67
 
68
68
  pre_norm: eqx.nn.GroupNorm
69
69
  proj: eqx.nn.Conv2d
@@ -78,31 +78,43 @@ class HWDConv(eqx.Module):
78
78
  kernel_size: int = 1,
79
79
  use_bias: bool = False,
80
80
  act: Callable = jax.nn.relu,
81
- mode: Literal["h_discard", "accurate"] = "accurate",
81
+ mode: Literal["h_discard", "band_grouped", "accurate"] = "accurate",
82
82
  dropout: float = 0.0,
83
83
  key: PRNGKeyArray,
84
84
  ):
85
85
  self.mode = mode
86
86
 
87
- # Channels entering the projection (and thus the pre-norm)
88
87
  channels_in = in_channels if mode == "h_discard" else 4 * in_channels
89
88
 
90
- # Choose groups so no group crosses sub-band boundaries.
91
- # For accurate mode: total_groups = 4 * groups_per_band, each band has groups_per_band groups.
92
- # For h_discard mode: total_groups = groups_per_band (single band).
89
+ # GroupNorm: align groups to sub-band boundaries
90
+ # groups_per_band divides in_channels; total_groups = 4 * groups_per_band for 4 bands
93
91
  groups_per_band = nearest_power_of_2_divisor(in_channels, 32)
94
92
  total_groups = groups_per_band if mode == "h_discard" else 4 * groups_per_band
95
-
96
93
  self.pre_norm = eqx.nn.GroupNorm(channels=channels_in, groups=total_groups)
97
94
 
95
+ # Projection 1x1 conv
96
+ # - accurate: dense projection (groups=1)
97
+ # - band_grouped: independent per-band projections (groups=4), no cross-band mixing
98
+ # - h_discard: single-band dense projection (groups=1)
99
+ proj_groups = 1
100
+ if mode == "band_grouped":
101
+ if out_channels % 4 != 0:
102
+ raise ValueError(
103
+ "band_grouped mode requires out_channels to be divisible by 4 "
104
+ "so each sub-band produces an equal share."
105
+ )
106
+ proj_groups = 4
107
+
98
108
  self.proj = eqx.nn.Conv2d(
99
- channels_in,
100
- out_channels,
109
+ in_channels=channels_in,
110
+ out_channels=out_channels,
101
111
  kernel_size=kernel_size,
102
112
  use_bias=use_bias,
103
113
  padding="SAME",
114
+ groups=proj_groups,
104
115
  key=key,
105
116
  )
117
+
106
118
  self.act = act
107
119
 
108
120
  self.dropout = eqx.nn.Dropout(dropout)
@@ -115,11 +127,13 @@ class HWDConv(eqx.Module):
115
127
  if self.mode == "h_discard":
116
128
  y = LL # (C, H/2, W/2)
117
129
  else:
130
+ # Keep sub-bands contiguous and ordered so GN groups and grouped conv line up
118
131
  y = jnp.concatenate([LL, HL, LH, HH], axis=0) # (4*C, H/2, W/2)
119
132
 
133
+ # Band-aligned normalization (no cross-band statistics)
120
134
  y = self.pre_norm(y)
121
135
 
122
- y = self.proj(y) # (out_ch, H/2, W/2)
136
+ y = self.proj(y) # (out_channels, H/2, W/2)
123
137
  y = self.act(y)
124
138
 
125
139
  y = self.dropout(y, inference=inference, key=key)
@@ -1 +0,0 @@
1
- __version__ = "0.4.6a2"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes