flaxdiff 0.2.9__tar.gz → 0.2.11__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 (77) hide show
  1. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/PKG-INFO +2 -4
  2. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/README.md +0 -2
  3. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/inference/utils.py +1 -1
  4. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/models/hilbert.py +25 -20
  5. flaxdiff-0.2.11/flaxdiff/models/ssm_dit.py +560 -0
  6. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/trainer/general_diffusion_trainer.py +20 -10
  7. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/trainer/simple_trainer.py +14 -12
  8. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff.egg-info/PKG-INFO +2 -4
  9. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff.egg-info/SOURCES.txt +1 -0
  10. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/pyproject.toml +2 -2
  11. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/__init__.py +0 -0
  12. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/data/__init__.py +0 -0
  13. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/data/benchmark_decord.py +0 -0
  14. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/data/dataloaders.py +0 -0
  15. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/data/dataset_map.py +0 -0
  16. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/data/online_loader.py +0 -0
  17. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/data/sources/audio_utils.py +0 -0
  18. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/data/sources/av_example.py +0 -0
  19. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/data/sources/av_utils.py +0 -0
  20. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/data/sources/base.py +0 -0
  21. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/data/sources/images.py +0 -0
  22. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/data/sources/utils.py +0 -0
  23. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/data/sources/videos.py +0 -0
  24. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/data/sources/voxceleb2.py +0 -0
  25. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/inference/__init__.py +0 -0
  26. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/inference/pipeline.py +0 -0
  27. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/inputs/__init__.py +0 -0
  28. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/inputs/encoders.py +0 -0
  29. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/metrics/__init__.py +0 -0
  30. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/metrics/common.py +0 -0
  31. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/metrics/images.py +0 -0
  32. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/metrics/inception.py +0 -0
  33. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/metrics/psnr.py +0 -0
  34. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/metrics/ssim.py +0 -0
  35. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/metrics/utils.py +0 -0
  36. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/models/__init__.py +0 -0
  37. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/models/attention.py +0 -0
  38. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/models/autoencoder/__init__.py +0 -0
  39. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/models/autoencoder/autoencoder.py +0 -0
  40. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/models/autoencoder/diffusers.py +0 -0
  41. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/models/autoencoder/simple_autoenc.py +0 -0
  42. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/models/common.py +0 -0
  43. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/models/favor_fastattn.py +0 -0
  44. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/models/general.py +0 -0
  45. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/models/simple_dit.py +0 -0
  46. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/models/simple_mmdit.py +0 -0
  47. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/models/simple_unet.py +0 -0
  48. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/models/simple_vit.py +0 -0
  49. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/models/unet_3d.py +0 -0
  50. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/models/unet_3d_blocks.py +0 -0
  51. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/models/vit_common.py +0 -0
  52. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/predictors/__init__.py +0 -0
  53. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/samplers/__init__.py +0 -0
  54. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/samplers/common.py +0 -0
  55. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/samplers/ddim.py +0 -0
  56. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/samplers/ddpm.py +0 -0
  57. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/samplers/euler.py +0 -0
  58. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/samplers/heun_sampler.py +0 -0
  59. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/samplers/multistep_dpm.py +0 -0
  60. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/samplers/rk4_sampler.py +0 -0
  61. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/schedulers/__init__.py +0 -0
  62. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/schedulers/common.py +0 -0
  63. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/schedulers/continuous.py +0 -0
  64. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/schedulers/cosine.py +0 -0
  65. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/schedulers/discrete.py +0 -0
  66. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/schedulers/exp.py +0 -0
  67. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/schedulers/karras.py +0 -0
  68. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/schedulers/linear.py +0 -0
  69. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/schedulers/sqrt.py +0 -0
  70. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/trainer/__init__.py +0 -0
  71. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/trainer/autoencoder_trainer.py +0 -0
  72. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/trainer/diffusion_trainer.py +0 -0
  73. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff/utils.py +0 -0
  74. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff.egg-info/dependency_links.txt +0 -0
  75. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff.egg-info/requires.txt +0 -0
  76. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/flaxdiff.egg-info/top_level.txt +0 -0
  77. {flaxdiff-0.2.9 → flaxdiff-0.2.11}/setup.cfg +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flaxdiff
3
- Version: 0.2.9
3
+ Version: 0.2.11
4
4
  Summary: A versatile and easy to understand Diffusion library
5
5
  Author-email: Ashish Kumar Singh <ashishkmr472@gmail.com>
6
- License-Expression: MIT
6
+ License: MIT
7
7
  Description-Content-Type: text/markdown
8
8
  Requires-Dist: flax>=0.8.4
9
9
  Requires-Dist: jax>=0.4.28
@@ -22,8 +22,6 @@ Requires-Dist: python-dotenv
22
22
 
23
23
  # ![](images/logo.jpeg "FlaxDiff")
24
24
 
25
- **This project is being used for the UMD Course project MSML 605: MLOps**
26
-
27
25
  **This project is partially supported by [Google TPU Research Cloud](https://sites.research.google/trc/about/). I would like to thank the Google Cloud TPU team for providing me with the resources to train the bigger text-conditional models in multi-host distributed settings.**
28
26
 
29
27
  ## A Versatile and simple Diffusion Library
@@ -1,7 +1,5 @@
1
1
  # ![](images/logo.jpeg "FlaxDiff")
2
2
 
3
- **This project is being used for the UMD Course project MSML 605: MLOps**
4
-
5
3
  **This project is partially supported by [Google TPU Research Cloud](https://sites.research.google/trc/about/). I would like to thank the Google Cloud TPU team for providing me with the resources to train the bigger text-conditional models in multi-host distributed settings.**
6
4
 
7
5
  ## A Versatile and simple Diffusion Library
@@ -121,8 +121,8 @@ def parse_config(config, overrides=None):
121
121
  'uvit': UViT,
122
122
  'diffusers_unet_simple': FlaxUNet2DConditionModel,
123
123
  'simple_dit': SimpleDiT,
124
- 'simple_uvit': SimpleUDiT,
125
124
  'simple_mmdit': SimpleMMDiT,
125
+ 'simple_udit': SimpleUDiT,
126
126
  }
127
127
 
128
128
  # Map all the leaves of the model config, converting strings to appropriate types
@@ -241,7 +241,6 @@ def hilbert_unpatchify(x: jnp.ndarray, inv_idx: jnp.ndarray, patch_size: int, H:
241
241
  target_shape = (B, total_patches_expected, patch_dim)
242
242
 
243
243
  # Create indices for gathering from x (Hilbert order h) based on inv_idx (map k -> h)
244
- # inv_idx contains the 'h' index for each 'k' index.
245
244
  # Clamp invalid indices (-1) to 0; we'll mask these results later.
246
245
  # Values must be < N (the actual number of patches in x).
247
246
  h_indices_for_gather = jnp.maximum(inv_idx, 0) # Shape [total_patches_expected]
@@ -320,26 +319,37 @@ def visualize_hilbert_curve(H: int, W: int, patch_size: int, figsize=(12, 5)):
320
319
  orig_grid = np.arange(H_P * W_P).reshape((H_P, W_P))
321
320
  im0 = axes[0].imshow(orig_grid, cmap='viridis', aspect='auto')
322
321
  axes[0].set_title(f"Original Grid ({H_P}x{W_P})\n(Row-Major Order)")
323
- # Add text labels for indices
324
- for r in range(H_P):
325
- for c in range(W_P):
326
- axes[0].text(c, r, f'{orig_grid[r, c]}', ha='center', va='center', color='white' if orig_grid[r,c] < (H_P*W_P)/2 else 'black', fontsize=8)
322
+ # Remove text labels for indices
327
323
  axes[0].set_xticks(np.arange(W_P))
328
324
  axes[0].set_yticks(np.arange(H_P))
329
325
  axes[0].set_xticklabels(np.arange(W_P))
330
326
  axes[0].set_yticklabels(np.arange(H_P))
331
327
  plt.colorbar(im0, ax=axes[0], fraction=0.046, pad=0.04, label="Row-Major Index")
332
328
 
329
+ # Draw the row-major scanning line
330
+ if H_P * W_P > 1:
331
+ row_major_coords_y = []
332
+ row_major_coords_x = []
333
+ for r_idx in range(H_P * W_P):
334
+ r = r_idx // W_P
335
+ c = r_idx % W_P
336
+ row_major_coords_y.append(r + 0.5) # Cell center
337
+ row_major_coords_x.append(c + 0.5) # Cell center
338
+ axes[0].plot(row_major_coords_x, row_major_coords_y, color='red', linestyle='-', linewidth=1.5, alpha=0.8)
339
+ axes[0].plot(row_major_coords_x[0], row_major_coords_y[0], 'go', markersize=5, label='Start') # Smaller marker
340
+ axes[0].plot(row_major_coords_x[-1], row_major_coords_y[-1], 'mo', markersize=5, label='End') # Smaller marker
341
+ axes[0].legend(fontsize='x-small')
342
+
333
343
  # --- Plot 2: Hilbert Curve Ordering ---
334
344
  # Mask unmapped cells for visualization
335
345
  masked_grid = np.ma.masked_where(grid == -1, grid)
336
346
  im1 = axes[1].imshow(masked_grid, cmap=cmap, aspect='auto', vmin=0, vmax=max(0, len(idx)-1))
337
347
  axes[1].set_title(f"Hilbert Curve Ordering ({len(idx)} points)")
338
348
  # Add text labels for Hilbert indices
339
- for r in range(H_P):
340
- for c in range(W_P):
341
- if grid[r,c] != -1:
342
- axes[1].text(c, r, f'{int(grid[r, c])}', ha='center', va='center', color='black', fontsize=8)
349
+ # for r in range(H_P):
350
+ # for c in range(W_P):
351
+ # if grid[r,c] != -1:
352
+ # axes[1].text(c, r, f'{int(grid[r, c])}', ha='center', va='center', color='black', fontsize=8)
343
353
  axes[1].set_xticks(np.arange(W_P))
344
354
  axes[1].set_yticks(np.arange(H_P))
345
355
  axes[1].set_xticklabels(np.arange(W_P))
@@ -355,22 +365,17 @@ def visualize_hilbert_curve(H: int, W: int, patch_size: int, figsize=(12, 5)):
355
365
  for i in range(len(idx)):
356
366
  if i in row_col_map:
357
367
  coords.append(row_col_map[i])
358
- # Fallback (slower):
359
- # row_indices, col_indices = np.where(grid == i)
360
- # if len(row_indices) > 0:
361
- # coords.append((row_indices[0], col_indices[0]))
362
368
 
363
369
  if coords:
364
370
  # Get coordinates for plotting (centers of cells)
365
- y_coords = [r + 0.5 for r, c in coords]
366
- x_coords = [c + 0.5 for r, c in coords]
367
- axes[1].plot(x_coords, y_coords, color='black', linestyle='-', linewidth=1.5, alpha=0.8)
371
+ y_coords = [r + 0.5 for r, c in coords] # Cell center
372
+ x_coords = [c + 0.5 for r, c in coords] # Cell center
373
+ axes[1].plot(x_coords, y_coords, color='red', linestyle='-', linewidth=1.5, alpha=0.8) # Ensure Hilbert curve is red
368
374
  # Mark start point
369
- axes[1].plot(x_coords[0], y_coords[0], 'go', markersize=8, label='Start (Idx 0)') # Green circle
375
+ axes[1].plot(x_coords[0], y_coords[0], 'go', markersize=5, label='Start') # Smaller marker
370
376
  # Mark end point
371
- axes[1].plot(x_coords[-1], y_coords[-1], 'mo', markersize=8, label=f'End (Idx {len(idx)-1})') # Magenta circle
372
- axes[1].legend(fontsize='small')
373
-
377
+ axes[1].plot(x_coords[-1], y_coords[-1], 'mo', markersize=5, label='End') # Smaller marker
378
+ axes[1].legend(fontsize='x-small')
374
379
 
375
380
  plt.tight_layout()
376
381
  return fig
@@ -0,0 +1,560 @@
1
+ """
2
+ SSM-based DiT Block and Hybrid SSM-Attention DiT for diffusion models.
3
+
4
+ Implements S5 (Simplified State Space) layers as a drop-in replacement for
5
+ attention in DiT blocks. The S5 layer uses diagonal state spaces with
6
+ JAX associative_scan for efficient parallel computation on TPUs.
7
+
8
+ References:
9
+ - Smith et al., "Simplified State Space Layers for Sequence Modeling", ICLR 2023
10
+ - Peebles & Xie, "Scalable Diffusion Models with Transformers", ICCV 2023
11
+ """
12
+
13
+ import jax
14
+ import jax.numpy as jnp
15
+ from flax import linen as nn
16
+ from typing import Callable, Any, Optional, Tuple, Sequence, Union
17
+ import einops
18
+ from functools import partial
19
+
20
+ from .vit_common import PatchEmbedding, unpatchify, RotaryEmbedding, RoPEAttention, AdaLNParams
21
+ from .common import kernel_init, FourierEmbedding, TimeProjection
22
+ from .attention import NormalAttention
23
+ from flax.typing import Dtype, PrecisionLike
24
+
25
+ from .hilbert import hilbert_indices, inverse_permutation, hilbert_patchify, hilbert_unpatchify
26
+ from .simple_dit import DiTBlock
27
+
28
+
29
+ # =============================================================================
30
+ # S5 SSM Layer - Diagonal State Space with Parallel Scan
31
+ # =============================================================================
32
+
33
+ def hippo_initializer(state_dim):
34
+ """Initialize A matrix using HiPPO-LegS (Legendre State Space) framework.
35
+
36
+ For diagonal S5, we use the diagonal approximation of HiPPO.
37
+ Returns complex diagonal elements that capture long-range dependencies.
38
+ """
39
+ def init(key, shape, dtype=jnp.float32):
40
+ # Diagonal HiPPO initialization: lambda_n = -(2n+1)/2 + i*pi*n
41
+ n = jnp.arange(state_dim)
42
+ real = -(n + 0.5)
43
+ imag = jnp.pi * n
44
+ # Normalize to unit circle for stability
45
+ lambda_init = real + 1j * imag
46
+ # Discretize with dt=1 for initialization, actual dt is learned
47
+ return lambda_init.astype(jnp.complex64)
48
+ return init
49
+
50
+
51
+ class S5Layer(nn.Module):
52
+ """S5 (Simplified State Space) layer with diagonal state matrix.
53
+
54
+ Processes sequences using state space recurrence:
55
+ x_k = A * x_{k-1} + B * u_k
56
+ y_k = Re(C * x_k) + D * u_k
57
+
58
+ Where A is diagonal (complex), enabling efficient parallel scan via
59
+ jax.lax.associative_scan. This is TPU-friendly as it maps to
60
+ prefix-sum operations on the systolic array.
61
+
62
+ Args:
63
+ features: Output dimension (must match input for residual)
64
+ state_dim: Dimension of the hidden state (per feature)
65
+ dt_min: Minimum discretization step (log scale)
66
+ dt_max: Maximum discretization step (log scale)
67
+ dtype: Computation dtype
68
+ precision: JAX precision level
69
+ """
70
+ features: int
71
+ state_dim: int = 64
72
+ dt_min: float = 0.001
73
+ dt_max: float = 0.1
74
+ dtype: Optional[Dtype] = None
75
+ precision: PrecisionLike = None
76
+
77
+ @nn.compact
78
+ def __call__(self, u):
79
+ """
80
+ Args:
81
+ u: Input sequence [B, S, F]
82
+ Returns:
83
+ y: Output sequence [B, S, F]
84
+ """
85
+ B, S, F = u.shape
86
+
87
+ # --- Learnable SSM Parameters ---
88
+ # A: Diagonal state matrix (complex) - initialized with HiPPO
89
+ # We parameterize as log of negative real part for stability
90
+ log_A_real = self.param(
91
+ 'log_A_real',
92
+ nn.initializers.normal(stddev=0.5),
93
+ (self.state_dim,)
94
+ )
95
+ A_imag = self.param(
96
+ 'A_imag',
97
+ nn.initializers.normal(stddev=0.5),
98
+ (self.state_dim,)
99
+ )
100
+
101
+ # B: Input-to-state projection [state_dim, F]
102
+ B_re = self.param(
103
+ 'B_re',
104
+ nn.initializers.lecun_normal(),
105
+ (self.state_dim, F)
106
+ )
107
+ B_im = self.param(
108
+ 'B_im',
109
+ nn.initializers.lecun_normal(),
110
+ (self.state_dim, F)
111
+ )
112
+
113
+ # C: State-to-output projection [F, state_dim]
114
+ C_re = self.param(
115
+ 'C_re',
116
+ nn.initializers.lecun_normal(),
117
+ (F, self.state_dim)
118
+ )
119
+ C_im = self.param(
120
+ 'C_im',
121
+ nn.initializers.lecun_normal(),
122
+ (F, self.state_dim)
123
+ )
124
+
125
+ # D: Skip connection (direct input-to-output)
126
+ D = self.param('D', nn.initializers.ones, (F,))
127
+
128
+ # dt: Discretization timestep (learned, per-feature)
129
+ log_dt = self.param(
130
+ 'log_dt',
131
+ lambda key, shape: jax.random.uniform(
132
+ key, shape,
133
+ minval=jnp.log(self.dt_min),
134
+ maxval=jnp.log(self.dt_max)
135
+ ),
136
+ (F,)
137
+ )
138
+ dt = jnp.exp(log_dt) # [F]
139
+
140
+ # --- Construct complex A and discretize ---
141
+ # A_diag: complex diagonal [state_dim]
142
+ A_real = -jnp.exp(log_A_real) # Ensure negative real part for stability
143
+ A_diag = A_real + 1j * A_imag # [state_dim]
144
+
145
+ # ZOH discretization: A_bar = exp(A * dt), B_bar = (A_bar - I) * A^{-1} * B
146
+ # For diagonal A, this simplifies element-wise
147
+ # dt broadcasts: [F] -> we use mean dt for state transition
148
+ dt_mean = jnp.mean(dt)
149
+ A_bar = jnp.exp(A_diag * dt_mean) # [state_dim], complex
150
+
151
+ # B as complex: [state_dim, F]
152
+ B_complex = B_re + 1j * B_im
153
+ # Discretized B: element-wise
154
+ B_bar = ((A_bar[:, None] - 1.0) / (A_diag[:, None] + 1e-8)) * B_complex # [state_dim, F]
155
+
156
+ # C as complex: [F, state_dim]
157
+ C_complex = C_re + 1j * C_im
158
+
159
+ # --- Parallel Scan (associative scan for TPU efficiency) ---
160
+ # For diagonal SSM: x_k = A_bar * x_{k-1} + B_bar @ u_k
161
+ # This can be computed via associative scan with the binary operator:
162
+ # (a1, b1) * (a2, b2) = (a1 * a2, a2 * b1 + b2)
163
+
164
+ # Compute per-step inputs: B_bar @ u_k for each timestep
165
+ # u: [B, S, F], B_bar: [state_dim, F]
166
+ # Bu: [B, S, state_dim] (complex)
167
+ u_float = u.astype(jnp.float32)
168
+ Bu = jnp.einsum('bsf,nf->bsn', u_float, B_bar) # [B, S, state_dim]
169
+
170
+ # A_bar is the same for all timesteps: [state_dim]
171
+ # Expand for scan: [B, S, state_dim]
172
+ A_bar_expanded = jnp.broadcast_to(A_bar[None, None, :], (B, S, self.state_dim))
173
+
174
+ # Associative scan elements: (a, b) where a=A_bar, b=Bu
175
+ def binary_operator(e1, e2):
176
+ a1, b1 = e1
177
+ a2, b2 = e2
178
+ return a1 * a2, a2 * b1 + b2
179
+
180
+ # Run parallel scan along sequence dimension (axis=1)
181
+ _, x_states = jax.lax.associative_scan(
182
+ binary_operator,
183
+ (A_bar_expanded, Bu),
184
+ axis=1
185
+ )
186
+ # x_states: [B, S, state_dim] (complex) - hidden states at each step
187
+
188
+ # --- Output computation ---
189
+ # y_k = Re(C @ x_k) + D * u_k
190
+ # C: [F, state_dim], x_states: [B, S, state_dim]
191
+ y_complex = jnp.einsum('fn,bsn->bsf', C_complex, x_states) # [B, S, F]
192
+ y = y_complex.real # Take real part: [B, S, F]
193
+
194
+ # Add skip connection
195
+ y = y + D[None, None, :] * u_float # [B, S, F]
196
+
197
+ # Cast back to input dtype
198
+ if self.dtype is not None:
199
+ y = y.astype(self.dtype)
200
+ else:
201
+ y = y.astype(u.dtype)
202
+
203
+ return y
204
+
205
+
206
+ # =============================================================================
207
+ # Bidirectional S5 - processes sequence in both directions
208
+ # =============================================================================
209
+
210
+ class BidirectionalS5Layer(nn.Module):
211
+ """Bidirectional S5 layer - runs forward and backward scans.
212
+
213
+ For diffusion models, spatial patches have no inherent directionality,
214
+ so bidirectional processing captures dependencies in both directions
215
+ along the serialization curve (Hilbert, raster, etc.).
216
+
217
+ Output is the sum of forward and backward scans, projected to features.
218
+ """
219
+ features: int
220
+ state_dim: int = 64
221
+ dt_min: float = 0.001
222
+ dt_max: float = 0.1
223
+ dtype: Optional[Dtype] = None
224
+ precision: PrecisionLike = None
225
+
226
+ @nn.compact
227
+ def __call__(self, u):
228
+ """
229
+ Args:
230
+ u: [B, S, F]
231
+ Returns:
232
+ y: [B, S, F]
233
+ """
234
+ # Forward scan
235
+ y_fwd = S5Layer(
236
+ features=self.features,
237
+ state_dim=self.state_dim,
238
+ dt_min=self.dt_min,
239
+ dt_max=self.dt_max,
240
+ dtype=self.dtype,
241
+ precision=self.precision,
242
+ name="s5_forward"
243
+ )(u)
244
+
245
+ # Backward scan (reverse input, scan, reverse output)
246
+ u_rev = jnp.flip(u, axis=1)
247
+ y_bwd_rev = S5Layer(
248
+ features=self.features,
249
+ state_dim=self.state_dim,
250
+ dt_min=self.dt_min,
251
+ dt_max=self.dt_max,
252
+ dtype=self.dtype,
253
+ precision=self.precision,
254
+ name="s5_backward"
255
+ )(u_rev)
256
+ y_bwd = jnp.flip(y_bwd_rev, axis=1)
257
+
258
+ # Combine forward and backward
259
+ y = y_fwd + y_bwd
260
+
261
+ # Output projection to mix directions
262
+ y = nn.Dense(
263
+ features=self.features,
264
+ dtype=self.dtype,
265
+ precision=self.precision,
266
+ name="out_proj"
267
+ )(y)
268
+
269
+ return y
270
+
271
+
272
+ # =============================================================================
273
+ # SSMDiTBlock - Drop-in replacement for DiTBlock
274
+ # =============================================================================
275
+
276
+ class SSMDiTBlock(nn.Module):
277
+ """SSM-based DiT Block that replaces attention with bidirectional S5.
278
+
279
+ Maintains the EXACT same interface as DiTBlock:
280
+ __call__(self, x, conditioning, freqs_cis) -> x
281
+
282
+ The AdaLN modulation, gating, residual connections, and MLP path
283
+ are IDENTICAL to DiTBlock. Only the attention is replaced with S5.
284
+
285
+ Note: freqs_cis (RoPE frequencies) is accepted for interface compatibility
286
+ but not used by the SSM - positional information is implicitly captured
287
+ by the sequential scan along the serialization order (Hilbert/raster/etc).
288
+ """
289
+ features: int
290
+ num_heads: int # Not used by SSM, kept for interface compat
291
+ rope_emb: RotaryEmbedding # Not used by SSM, kept for interface compat
292
+ state_dim: int = 64
293
+ mlp_ratio: int = 4
294
+ dropout_rate: float = 0.0
295
+ dtype: Optional[Dtype] = None
296
+ precision: PrecisionLike = None
297
+ use_flash_attention: bool = False # Ignored, interface compat
298
+ force_fp32_for_softmax: bool = True # Ignored, interface compat
299
+ norm_epsilon: float = 1e-5
300
+ use_gating: bool = True
301
+ bidirectional: bool = True
302
+
303
+ def setup(self):
304
+ hidden_features = int(self.features * self.mlp_ratio)
305
+
306
+ # AdaLN modulation - IDENTICAL to DiTBlock
307
+ self.ada_params_module = AdaLNParams(
308
+ self.features, dtype=self.dtype, precision=self.precision)
309
+
310
+ # Layer Norms - IDENTICAL to DiTBlock
311
+ self.norm1 = nn.LayerNorm(
312
+ epsilon=self.norm_epsilon, use_scale=False, use_bias=False,
313
+ dtype=self.dtype, name="norm1")
314
+ self.norm2 = nn.LayerNorm(
315
+ epsilon=self.norm_epsilon, use_scale=False, use_bias=False,
316
+ dtype=self.dtype, name="norm2")
317
+
318
+ # S5 SSM layer (replaces attention)
319
+ ssm_cls = BidirectionalS5Layer if self.bidirectional else S5Layer
320
+ self.ssm = ssm_cls(
321
+ features=self.features,
322
+ state_dim=self.state_dim,
323
+ dtype=self.dtype,
324
+ precision=self.precision,
325
+ name="ssm"
326
+ )
327
+
328
+ # MLP - IDENTICAL to DiTBlock
329
+ self.mlp = nn.Sequential([
330
+ nn.Dense(features=hidden_features, dtype=self.dtype, precision=self.precision),
331
+ nn.gelu,
332
+ nn.Dense(features=self.features, dtype=self.dtype, precision=self.precision)
333
+ ])
334
+
335
+ @nn.compact
336
+ def __call__(self, x, conditioning, freqs_cis):
337
+ """Exact same signature as DiTBlock.__call__"""
338
+ # Get scale/shift/gate parameters - IDENTICAL to DiTBlock
339
+ scale_mlp, shift_mlp, gate_mlp, scale_attn, shift_attn, gate_attn = jnp.split(
340
+ self.ada_params_module(conditioning), 6, axis=-1
341
+ )
342
+
343
+ # --- SSM Path (replaces Attention Path) ---
344
+ residual = x
345
+ norm_x = self.norm1(x)
346
+ x_modulated = norm_x * (1 + scale_attn) + shift_attn
347
+ ssm_output = self.ssm(x_modulated)
348
+
349
+ if self.use_gating:
350
+ x = residual + gate_attn * ssm_output
351
+ else:
352
+ x = residual + ssm_output
353
+
354
+ # --- MLP Path --- IDENTICAL to DiTBlock
355
+ residual = x
356
+ norm_x_mlp = self.norm2(x)
357
+ x_mlp_modulated = norm_x_mlp * (1 + scale_mlp) + shift_mlp
358
+ mlp_output = self.mlp(x_mlp_modulated)
359
+
360
+ if self.use_gating:
361
+ x = residual + gate_mlp * mlp_output
362
+ else:
363
+ x = residual + mlp_output
364
+
365
+ return x
366
+
367
+
368
+ # =============================================================================
369
+ # HybridSSMAttentionDiT - The proposed novel architecture
370
+ # =============================================================================
371
+
372
+ class HybridSSMAttentionDiT(nn.Module):
373
+ """Hybrid SSM-Attention Diffusion Transformer.
374
+
375
+ Interleaves SSM blocks (for O(n) local processing along Hilbert curve)
376
+ with attention blocks (for O(n^2) global composition) in a configurable
377
+ ratio. Both block types share the same AdaLN conditioning interface.
378
+
379
+ Args:
380
+ block_pattern: List of 'ssm' or 'attn' strings defining the block sequence.
381
+ Example: ['ssm','ssm','ssm','attn'] for 3:1 ratio repeated.
382
+ If None, uses ssm_attention_ratio to auto-generate pattern.
383
+ ssm_attention_ratio: Shorthand ratio like '3:1' (3 SSM per 1 attention).
384
+ Only used if block_pattern is None.
385
+ """
386
+ output_channels: int = 3
387
+ patch_size: int = 16
388
+ emb_features: int = 768
389
+ num_layers: int = 12
390
+ num_heads: int = 12
391
+ mlp_ratio: int = 4
392
+ ssm_state_dim: int = 64
393
+ dropout_rate: float = 0.0
394
+ dtype: Optional[Dtype] = None
395
+ precision: PrecisionLike = None
396
+ use_flash_attention: bool = False
397
+ force_fp32_for_softmax: bool = True
398
+ norm_epsilon: float = 1e-5
399
+ learn_sigma: bool = False
400
+ use_hilbert: bool = False
401
+ norm_groups: int = 0
402
+ activation: Callable = jax.nn.swish
403
+ block_pattern: Optional[Sequence[str]] = None # e.g., ['ssm','ssm','ssm','attn']
404
+ ssm_attention_ratio: str = "3:1" # e.g., "3:1", "1:1", "all-ssm", "all-attn"
405
+ bidirectional_ssm: bool = True
406
+
407
+ def _build_block_pattern(self):
408
+ """Generate block pattern from ratio string."""
409
+ if self.block_pattern is not None:
410
+ pattern = list(self.block_pattern)
411
+ elif self.ssm_attention_ratio == "all-ssm":
412
+ pattern = ['ssm'] * self.num_layers
413
+ elif self.ssm_attention_ratio == "all-attn":
414
+ pattern = ['attn'] * self.num_layers
415
+ else:
416
+ parts = self.ssm_attention_ratio.split(':')
417
+ n_ssm, n_attn = int(parts[0]), int(parts[1])
418
+ unit = ['ssm'] * n_ssm + ['attn'] * n_attn
419
+ pattern = (unit * (self.num_layers // len(unit) + 1))[:self.num_layers]
420
+ return pattern
421
+
422
+ def setup(self):
423
+ self.patch_embed = PatchEmbedding(
424
+ patch_size=self.patch_size,
425
+ embedding_dim=self.emb_features,
426
+ dtype=self.dtype,
427
+ precision=self.precision
428
+ )
429
+
430
+ if self.use_hilbert:
431
+ self.hilbert_proj = nn.Dense(
432
+ features=self.emb_features,
433
+ dtype=self.dtype,
434
+ precision=self.precision,
435
+ name="hilbert_projection"
436
+ )
437
+
438
+ # Time embedding
439
+ self.time_embed = nn.Sequential([
440
+ FourierEmbedding(features=self.emb_features),
441
+ TimeProjection(features=self.emb_features * self.mlp_ratio),
442
+ nn.Dense(features=self.emb_features, dtype=self.dtype, precision=self.precision)
443
+ ])
444
+
445
+ # Text context projection
446
+ self.text_proj = nn.Dense(
447
+ features=self.emb_features, dtype=self.dtype,
448
+ precision=self.precision, name="text_context_proj")
449
+
450
+ # RoPE (used by attention blocks, passed through SSM blocks)
451
+ self.rope = RotaryEmbedding(
452
+ dim=self.emb_features // self.num_heads,
453
+ max_seq_len=4096, dtype=self.dtype)
454
+
455
+ # Build hybrid block sequence
456
+ pattern = self._build_block_pattern()
457
+ blocks = []
458
+ for i, block_type in enumerate(pattern):
459
+ if block_type == 'ssm':
460
+ blocks.append(SSMDiTBlock(
461
+ features=self.emb_features,
462
+ num_heads=self.num_heads,
463
+ rope_emb=self.rope,
464
+ state_dim=self.ssm_state_dim,
465
+ mlp_ratio=self.mlp_ratio,
466
+ dropout_rate=self.dropout_rate,
467
+ dtype=self.dtype,
468
+ precision=self.precision,
469
+ norm_epsilon=self.norm_epsilon,
470
+ bidirectional=self.bidirectional_ssm,
471
+ name=f"ssm_block_{i}"
472
+ ))
473
+ else: # 'attn'
474
+ blocks.append(DiTBlock(
475
+ features=self.emb_features,
476
+ num_heads=self.num_heads,
477
+ rope_emb=self.rope,
478
+ mlp_ratio=self.mlp_ratio,
479
+ dropout_rate=self.dropout_rate,
480
+ dtype=self.dtype,
481
+ precision=self.precision,
482
+ use_flash_attention=self.use_flash_attention,
483
+ force_fp32_for_softmax=self.force_fp32_for_softmax,
484
+ norm_epsilon=self.norm_epsilon,
485
+ name=f"dit_block_{i}"
486
+ ))
487
+ self.blocks = blocks
488
+
489
+ # Final layer
490
+ self.final_norm = nn.LayerNorm(
491
+ epsilon=self.norm_epsilon, dtype=self.dtype, name="final_norm")
492
+
493
+ output_dim = self.patch_size * self.patch_size * self.output_channels
494
+ if self.learn_sigma:
495
+ output_dim *= 2
496
+
497
+ self.final_proj = nn.Dense(
498
+ features=output_dim,
499
+ dtype=self.dtype,
500
+ precision=self.precision,
501
+ kernel_init=nn.initializers.zeros,
502
+ name="final_proj"
503
+ )
504
+
505
+ @nn.compact
506
+ def __call__(self, x, temb, textcontext=None):
507
+ """Exact same signature as SimpleDiT.__call__"""
508
+ B, H, W, C = x.shape
509
+ assert H % self.patch_size == 0 and W % self.patch_size == 0
510
+
511
+ H_P = H // self.patch_size
512
+ W_P = W // self.patch_size
513
+
514
+ # 1. Patch Embedding (identical to SimpleDiT)
515
+ if self.use_hilbert:
516
+ patches_raw, hilbert_inv_idx = hilbert_patchify(x, self.patch_size)
517
+ patches = self.hilbert_proj(patches_raw)
518
+ else:
519
+ patches = self.patch_embed(x)
520
+ hilbert_inv_idx = None
521
+
522
+ num_patches = patches.shape[1]
523
+ x_seq = patches
524
+
525
+ # 2. Conditioning (identical to SimpleDiT)
526
+ t_emb = self.time_embed(temb)
527
+ cond_emb = t_emb
528
+ if textcontext is not None:
529
+ text_emb = self.text_proj(textcontext)
530
+ text_emb_pooled = jnp.mean(text_emb, axis=1)
531
+ cond_emb = cond_emb + text_emb_pooled
532
+
533
+ # 3. RoPE frequencies
534
+ freqs_cos, freqs_sin = self.rope(seq_len=num_patches)
535
+
536
+ # 4. Hybrid blocks (SSM and attention interleaved)
537
+ for block in self.blocks:
538
+ x_seq = block(x_seq, conditioning=cond_emb, freqs_cis=(freqs_cos, freqs_sin))
539
+
540
+ # 5. Final output (identical to SimpleDiT)
541
+ x_out = self.final_norm(x_seq)
542
+ x_out = self.final_proj(x_out)
543
+
544
+ # 6. Unpatchify
545
+ if self.use_hilbert:
546
+ if self.learn_sigma:
547
+ x_mean, x_logvar = jnp.split(x_out, 2, axis=-1)
548
+ x_image = hilbert_unpatchify(x_mean, hilbert_inv_idx, self.patch_size, H, W, self.output_channels)
549
+ return x_image
550
+ else:
551
+ x_image = hilbert_unpatchify(x_out, hilbert_inv_idx, self.patch_size, H, W, self.output_channels)
552
+ return x_image
553
+ else:
554
+ if self.learn_sigma:
555
+ x_mean, x_logvar = jnp.split(x_out, 2, axis=-1)
556
+ x = unpatchify(x_mean, channels=self.output_channels)
557
+ return x
558
+ else:
559
+ x = unpatchify(x_out, channels=self.output_channels)
560
+ return x
@@ -427,7 +427,7 @@ class GeneralDiffusionTrainer(DiffusionTrainer):
427
427
  process_index = jax.process_index()
428
428
  generate_samples = val_step_fn
429
429
 
430
- val_ds = iter(val_ds) if val_ds else None
430
+ val_ds = iter(val_ds()) if val_ds else None
431
431
  print(f"Validation loop started for process index {process_index} with {global_device_count} devices.")
432
432
  # Evaluation step
433
433
  try:
@@ -475,10 +475,11 @@ class GeneralDiffusionTrainer(DiffusionTrainer):
475
475
  metrics = {k: np.mean(v) for k, v in metrics.items()}
476
476
  # Update the best validation metrics
477
477
  for key, value in metrics.items():
478
- if key not in self.best_val_metrics:
479
- self.best_val_metrics[key] = value
478
+ final_key = f"val/{key}"
479
+ if final_key not in self.best_val_metrics:
480
+ self.best_val_metrics[final_key] = value
480
481
  else:
481
- self.best_val_metrics[key] = min(self.best_val_metrics[key], value)
482
+ self.best_val_metrics[final_key] = min(self.best_val_metrics[final_key], value)
482
483
  # Log the best validation metrics
483
484
  if getattr(self, 'wandb', None) is not None and self.wandb:
484
485
  # Log the metrics
@@ -488,6 +489,13 @@ class GeneralDiffusionTrainer(DiffusionTrainer):
488
489
  self.wandb.log({
489
490
  f"val/{key}": value,
490
491
  }, step=current_step)
492
+ # Log the best validation metrics
493
+ for key, value in self.best_val_metrics.items():
494
+ if isinstance(value, jnp.ndarray):
495
+ value = np.array(value)
496
+ self.wandb.log({
497
+ f"best_{key}": value,
498
+ }, step=current_step)
491
499
  print(f"Validation metrics for process index {process_index}: {metrics}")
492
500
 
493
501
  # Close validation dataset iterator
@@ -622,10 +630,10 @@ class GeneralDiffusionTrainer(DiffusionTrainer):
622
630
  if not runs:
623
631
  raise ValueError("No runs found in wandb.")
624
632
  print(f"Getting best runs from wandb {self.wandb.id}...")
625
- runs = sorted(runs, key=lambda x: x.summary.get(metric, float('inf')))
633
+ runs = sorted(runs, key=lambda x: x.summary.get(f"best_{metric}", float('inf')))
626
634
  best_runs = runs[:top_k]
627
- lower_bound = best_runs[-1].summary.get(metric, float('inf'))
628
- upper_bound = best_runs[0].summary.get(metric, float('inf'))
635
+ lower_bound = best_runs[-1].summary.get(f"best_{metric}", float('inf'))
636
+ upper_bound = best_runs[0].summary.get(f"best_{metric}", float('inf'))
629
637
  print(f"Best runs from wandb {self.wandb.id}:")
630
638
  for run in best_runs:
631
639
  print(f"\t\tRun ID: {run.id}, Metric: {run.summary.get(metric, float('inf'))}")
@@ -649,19 +657,21 @@ class GeneralDiffusionTrainer(DiffusionTrainer):
649
657
  best_runs, bounds = self.__get_best_general_runs__(metric=metric, top_k=top_k)
650
658
 
651
659
  # Determine if lower or higher values are better (for loss, lower is better)
652
- is_lower_better = "loss" in metric.lower()
660
+ is_lower_better = True
653
661
 
654
662
  # Check if current run is one of the best
655
663
  if metric == "train/best_loss":
656
664
  current_run_metric = self.best_loss
657
665
  elif metric in self.best_val_metrics:
666
+ print(f"Fetching best validation metric {metric} from local")
658
667
  current_run_metric = self.best_val_metrics[metric]
659
668
  else:
660
669
  current_run_metric = self.wandb.summary.get(metric, float('inf') if is_lower_better else float('-inf'))
661
-
670
+
671
+ print(f"Current run {self.wandb.id} metric: {current_run_metric}, Best bounds: {bounds}")
662
672
  # Check based on bounds
663
673
  if (is_lower_better and current_run_metric < bounds[1]) or (not is_lower_better and current_run_metric > bounds[0]):
664
- print(f"Current run {self.wandb.id} meets performance criteria.")
674
+ print(f"Current run {self.wandb.id} meets performance criteria. Current metric: {current_run_metric}, Best bounds: {bounds}")
665
675
  is_best = (is_lower_better and current_run_metric < bounds[0]) or (not is_lower_better and current_run_metric > bounds[1])
666
676
  return True, is_best
667
677
 
@@ -600,7 +600,7 @@ class SimpleTrainer:
600
600
 
601
601
  def fit(self, data, train_steps_per_epoch, epochs, train_step_args={}, val_steps_per_epoch=5, validation_step_args={}):
602
602
  train_ds = iter(data['train']())
603
- val_ds = data.get('val', data.get('test', None))()
603
+ val_ds = data.get('val', data.get('test', None))
604
604
  train_step = self._define_train_step(**train_step_args)
605
605
  val_step = self._define_validation_step(**validation_step_args)
606
606
  train_state = self.state
@@ -642,6 +642,19 @@ class SimpleTrainer:
642
642
  self.rngstate = rng_state
643
643
  total_time = end_time - start_time
644
644
  avg_time_per_step = total_time / train_steps_per_epoch
645
+
646
+ if val_steps_per_epoch > 0:
647
+ print(f"Validation started for process index {process_index}")
648
+ # Validation step
649
+ self.validation_loop(
650
+ train_state,
651
+ val_step,
652
+ val_ds,
653
+ val_steps_per_epoch,
654
+ current_step,
655
+ )
656
+ print(colored(f"Validation done on process index {process_index}", PROCESS_COLOR_MAP[process_index]))
657
+
645
658
  avg_loss = epoch_loss / train_steps_per_epoch
646
659
  if avg_loss < self.best_loss:
647
660
  self.best_loss = avg_loss
@@ -659,17 +672,6 @@ class SimpleTrainer:
659
672
  }, step=current_step)
660
673
  print(colored(f"\n\tEpoch {current_epoch} completed. Avg Loss: {avg_loss}, Time: {total_time:.2f}s, Best Loss: {self.best_loss}", 'green'))
661
674
 
662
- if val_steps_per_epoch > 0:
663
- print(f"Validation started for process index {process_index}")
664
- # Validation step
665
- self.validation_loop(
666
- train_state,
667
- val_step,
668
- val_ds,
669
- val_steps_per_epoch,
670
- current_step,
671
- )
672
- print(colored(f"Validation done on process index {process_index}", PROCESS_COLOR_MAP[process_index]))
673
675
 
674
676
  self.save(epochs)#
675
677
  return self.state
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flaxdiff
3
- Version: 0.2.9
3
+ Version: 0.2.11
4
4
  Summary: A versatile and easy to understand Diffusion library
5
5
  Author-email: Ashish Kumar Singh <ashishkmr472@gmail.com>
6
- License-Expression: MIT
6
+ License: MIT
7
7
  Description-Content-Type: text/markdown
8
8
  Requires-Dist: flax>=0.8.4
9
9
  Requires-Dist: jax>=0.4.28
@@ -22,8 +22,6 @@ Requires-Dist: python-dotenv
22
22
 
23
23
  # ![](images/logo.jpeg "FlaxDiff")
24
24
 
25
- **This project is being used for the UMD Course project MSML 605: MLOps**
26
-
27
25
  **This project is partially supported by [Google TPU Research Cloud](https://sites.research.google/trc/about/). I would like to thank the Google Cloud TPU team for providing me with the resources to train the bigger text-conditional models in multi-host distributed settings.**
28
26
 
29
27
  ## A Versatile and simple Diffusion Library
@@ -42,6 +42,7 @@ flaxdiff/models/simple_dit.py
42
42
  flaxdiff/models/simple_mmdit.py
43
43
  flaxdiff/models/simple_unet.py
44
44
  flaxdiff/models/simple_vit.py
45
+ flaxdiff/models/ssm_dit.py
45
46
  flaxdiff/models/unet_3d.py
46
47
  flaxdiff/models/unet_3d_blocks.py
47
48
  flaxdiff/models/vit_common.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "flaxdiff"
7
- version = "0.2.9"
7
+ version = "0.2.11"
8
8
  description = "A versatile and easy to understand Diffusion library"
9
9
  readme = "README.md"
10
10
  authors = [
@@ -26,7 +26,7 @@ dependencies = [
26
26
  "rich",
27
27
  "python-dotenv",
28
28
  ]
29
- license = "MIT"
29
+ license = {text = "MIT"}
30
30
 
31
31
  [tool.setuptools.packages.find]
32
32
  include = ["flaxdiff*"]
File without changes
File without changes
File without changes