diffsynth 2.1.12__py3-none-any.whl → 2.2.0__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.
@@ -1 +1,2 @@
1
- from .attention import attention_forward, FLEX_ATTN_AVAILABLE
1
+ from .attention import attention_forward, list_attention_implementations
2
+ from .flex_attention import FLEX_ATTN_AVAILABLE
@@ -1,255 +1,51 @@
1
- import torch, os, inspect
2
- from einops import rearrange, repeat
3
-
4
- if os.environ.get("DIFFSYNTH_FLASH_ATTN_KERNEL_REPO_ID") is not None:
5
- try:
6
- from kernels import get_kernel
7
- kernel_module = get_kernel(os.environ.get("DIFFSYNTH_FLASH_ATTN_KERNEL_REPO_ID"), version=int(os.environ.get("DIFFSYNTH_FLASH_ATTN_KERNEL_VERSION")))
8
- customized_flash_attn_func = kernel_module.flash_attn_func
9
- CUSTOMIZED_FA_KERNEL_AVAILABLE = True
10
- except Exception:
11
- CUSTOMIZED_FA_KERNEL_AVAILABLE = False
12
- else:
13
- CUSTOMIZED_FA_KERNEL_AVAILABLE = False
14
-
15
- try:
16
- from flash_attn.cute import flash_attn_func as flash_attn_func_cute
17
- FLASH_ATTN_4_AVAILABLE = True
18
- except Exception:
19
- FLASH_ATTN_4_AVAILABLE = False
20
-
21
- try:
22
- import flash_attn_interface
23
- FLASH_ATTN_3_AVAILABLE = True
24
- except Exception:
25
- FLASH_ATTN_3_AVAILABLE = False
26
-
27
- try:
28
- import flash_attn
29
- FLASH_ATTN_2_AVAILABLE = True
30
- except Exception:
31
- FLASH_ATTN_2_AVAILABLE = False
32
-
33
- try:
34
- from sageattention import sageattn
35
- SAGE_ATTN_AVAILABLE = True
36
- except Exception:
37
- SAGE_ATTN_AVAILABLE = False
38
-
39
- try:
40
- import xformers.ops as xops
41
- XFORMERS_AVAILABLE = True
42
- except Exception:
43
- XFORMERS_AVAILABLE = False
44
-
45
- try:
46
- from torch.nn.attention.flex_attention import flex_attention as flex_attention_func
47
- flex_attention_func = torch.compile(flex_attention_func)
48
- FLEX_ATTN_AVAILABLE = True
49
- except Exception:
50
- FLEX_ATTN_AVAILABLE = False
51
-
52
- try:
53
- if "enable_gqa" in inspect.signature(torch.nn.functional.scaled_dot_product_attention).parameters:
54
- TORCH_SUPPORT_GQA = True
55
- else:
56
- TORCH_SUPPORT_GQA = False
57
- except Exception:
58
- TORCH_SUPPORT_GQA = False
1
+ import os
2
+ from .registry import (
3
+ get_attention_implementation,
4
+ list_attention_implementations,
5
+ register_attention_implementation,
6
+ )
7
+ from .flash_attention import CUSTOMIZED_FA_KERNEL_AVAILABLE, FLASH_ATTN_4_AVAILABLE, FLASH_ATTN_3_AVAILABLE, FLASH_ATTN_2_AVAILABLE
8
+ from .sage_attention import SAGE_ATTN_AVAILABLE
9
+ from .xformers_attention import XFORMERS_AVAILABLE
10
+ from .torch_attention import torch_sdpa, torch_sdpa_sliding_window
11
+ from .flex_attention import flex_attention
59
12
 
60
13
 
61
14
  def initialize_attention_priority():
62
15
  if os.environ.get('DIFFSYNTH_ATTENTION_IMPLEMENTATION') is not None:
63
- return os.environ.get('DIFFSYNTH_ATTENTION_IMPLEMENTATION').lower()
64
- elif CUSTOMIZED_FA_KERNEL_AVAILABLE:
65
- return "customized_fa_kernel"
66
- elif FLASH_ATTN_4_AVAILABLE:
67
- return "flash_attention_4"
68
- elif FLASH_ATTN_3_AVAILABLE:
69
- return "flash_attention_3"
70
- elif FLASH_ATTN_2_AVAILABLE:
71
- return "flash_attention_2"
72
- elif SAGE_ATTN_AVAILABLE:
73
- return "sage_attention"
74
- elif XFORMERS_AVAILABLE:
75
- return "xformers"
16
+ return os.environ.get('DIFFSYNTH_ATTENTION_IMPLEMENTATION').strip().lower()
76
17
  else:
18
+ for name in ["customized_fa_kernel", "flash_attention_4", "flash_attention_3", "flash_attention_2", "sage_attention", "xformers"]:
19
+ if name in list_attention_implementations():
20
+ return name
77
21
  return "torch"
78
22
 
79
23
 
80
24
  ATTENTION_IMPLEMENTATION = initialize_attention_priority()
81
25
 
82
26
 
83
- def rearrange_qkv(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", required_in_pattern="b n s d", dims=None):
84
- dims = {} if dims is None else dims
85
- if q_pattern != required_in_pattern:
86
- q = rearrange(q, f"{q_pattern} -> {required_in_pattern}", **dims)
87
- if k_pattern != required_in_pattern:
88
- k = rearrange(k, f"{k_pattern} -> {required_in_pattern}", **dims)
89
- if v_pattern != required_in_pattern:
90
- v = rearrange(v, f"{v_pattern} -> {required_in_pattern}", **dims)
91
- return q, k, v
92
-
93
-
94
- def rearrange_out(out: torch.Tensor, out_pattern="b n s d", required_out_pattern="b n s d", dims=None):
95
- dims = {} if dims is None else dims
96
- if out_pattern != required_out_pattern:
97
- out = rearrange(out, f"{required_out_pattern} -> {out_pattern}", **dims)
98
- return out
99
-
100
-
101
- def torch_sdpa(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, attn_mask=None, scale=None, is_causal=False):
102
- required_in_pattern, required_out_pattern= "b n s d", "b n s d"
103
- q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
104
- if q.shape[1] != k.shape[1] or q.shape[1] != v.shape[1]:
105
- # Grouped Query Attention
106
- if TORCH_SUPPORT_GQA:
107
- out = torch.nn.functional.scaled_dot_product_attention(q, k, v, attn_mask, scale=scale, is_causal=is_causal, enable_gqa=True)
108
- else:
109
- # In low-version torch, `enable_gqa` is not supported.
110
- k = repeat(k, "b n s d -> b (n m) s d", m=q.shape[1]//k.shape[1])
111
- v = repeat(v, "b n s d -> b (n m) s d", m=q.shape[1]//v.shape[1])
112
- out = torch.nn.functional.scaled_dot_product_attention(q, k, v, attn_mask, scale=scale, is_causal=is_causal)
113
- else:
114
- out = torch.nn.functional.scaled_dot_product_attention(q, k, v, attn_mask, scale=scale, is_causal=is_causal)
115
- out = rearrange_out(out, out_pattern, required_out_pattern, dims)
116
- return out
117
-
118
-
119
- def torch_sdpa_sliding_window(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, sliding_window: int, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, scale=None):
120
- required_in_pattern, required_out_pattern = "b n s d", "b n s d"
121
- q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
122
-
123
- B, N, S, D = q.shape
124
- W = sliding_window
125
- chunk_size = W
126
- num_chunks = (S + chunk_size - 1) // chunk_size
127
-
128
- output = torch.empty_like(q)
129
- dtype = q.dtype
130
- device = q.device
131
- min_val = torch.finfo(dtype).min
132
-
133
- for i in range(num_chunks):
134
- q_start = i * chunk_size
135
- q_end = min(q_start + chunk_size, S)
136
- actual_chunk_size = q_end - q_start
27
+ def attention_forward(q, k, v, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, attn_mask=None, scale=None, is_causal=False, compatibility_mode=False, window_size=None, use_flex=False, score_mod=None):
28
+ if use_flex or score_mod is not None:
29
+ return flex_attention(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, attn_mask=attn_mask, scale=scale, score_mod=score_mod)
137
30
 
138
- kv_start = max(0, q_start - W)
139
- kv_end = min(S, q_end + W)
31
+ if compatibility_mode:
32
+ return torch_sdpa(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, attn_mask=attn_mask, scale=scale, is_causal=is_causal, window_size=window_size)
140
33
 
141
- q_chunk = q[:, :, q_start:q_end, :]
142
- k_chunk = k[:, :, kv_start:kv_end, :]
143
- v_chunk = v[:, :, kv_start:kv_end, :]
34
+ backend = get_attention_implementation(ATTENTION_IMPLEMENTATION)
144
35
 
145
- q_indices = torch.arange(q_start, q_end, device=device)
146
- k_indices = torch.arange(kv_start, kv_end, device=device)
147
- diff = q_indices.unsqueeze(1) - k_indices.unsqueeze(0)
148
- valid = diff.abs() <= W
36
+ if attn_mask is not None and not backend.supports_mask:
37
+ return torch_sdpa(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, attn_mask=attn_mask, scale=scale, is_causal=is_causal)
38
+ if window_size is not None and not backend.supports_window:
39
+ assert is_causal == False and attn_mask is None
40
+ return torch_sdpa_sliding_window(q, k, v, window_size, q_pattern, k_pattern, v_pattern, out_pattern, dims, scale=scale)
41
+ if is_causal and not backend.supports_causal:
42
+ return torch_sdpa(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, attn_mask=attn_mask, scale=scale, is_causal=is_causal)
149
43
 
150
- local_mask = torch.zeros(actual_chunk_size, kv_end - kv_start, dtype=dtype, device=device)
151
- local_mask.masked_fill_(~valid, min_val)
152
- local_mask = local_mask.unsqueeze(0).unsqueeze(0)
153
-
154
- out_chunk = torch_sdpa(
155
- q_chunk, k_chunk, v_chunk, attn_mask=local_mask, scale=scale
156
- )
157
- output[:, :, q_start:q_end, :] = out_chunk
158
-
159
- output = rearrange_out(output, out_pattern, required_out_pattern, dims)
160
- return output
161
-
162
-
163
- def flash_attention_4(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, scale=None, is_causal=False, window_size=None):
164
- required_in_pattern, required_out_pattern= "b s n d", "b s n d"
165
- q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
166
- window_size = (window_size, window_size) if window_size is not None else (-1, -1)
167
- out = flash_attn_func_cute(q, k, v, softmax_scale=scale, causal=is_causal, window_size=window_size)
168
- if isinstance(out, tuple):
169
- out = out[0]
170
- out = rearrange_out(out, out_pattern, required_out_pattern, dims)
171
- return out
172
-
173
-
174
- def flash_attention_3(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, scale=None, is_causal=False, window_size=None):
175
- required_in_pattern, required_out_pattern= "b s n d", "b s n d"
176
- q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
177
- window_size = (window_size, window_size) if window_size is not None else (-1, -1)
178
- out = flash_attn_interface.flash_attn_func(q, k, v, softmax_scale=scale, causal=is_causal, window_size=window_size)
179
- if isinstance(out, tuple):
180
- out = out[0]
181
- out = rearrange_out(out, out_pattern, required_out_pattern, dims)
182
- return out
183
-
184
-
185
- def flash_attention_2(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, scale=None, is_causal=False, window_size=None):
186
- required_in_pattern, required_out_pattern= "b s n d", "b s n d"
187
- q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
188
- window_size = (window_size, window_size) if window_size is not None else (-1, -1)
189
- out = flash_attn.flash_attn_func(q, k, v, softmax_scale=scale, causal=is_causal, window_size=window_size)
190
- out = rearrange_out(out, out_pattern, required_out_pattern, dims)
191
- return out
192
-
193
-
194
- def flash_attention_customized_kernel(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, scale=None, is_causal=False, window_size=None):
195
- required_in_pattern, required_out_pattern = "b s n d", "b s n d"
196
- q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
197
- window_size = (window_size, window_size) if window_size is not None else (-1, -1)
198
- out = customized_flash_attn_func(q, k, v, softmax_scale=scale, causal=is_causal, window_size=window_size)
199
- if isinstance(out, tuple):
200
- out = out[0]
201
- out = rearrange_out(out, out_pattern, required_out_pattern, dims)
202
- return out
203
-
204
-
205
- def sage_attention(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, scale=None):
206
- required_in_pattern, required_out_pattern= "b n s d", "b n s d"
207
- q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
208
- out = sageattn(q, k, v, sm_scale=scale)
209
- out = rearrange_out(out, out_pattern, required_out_pattern, dims)
210
- return out
211
-
212
-
213
- def xformers_attention(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, scale=None):
214
- required_in_pattern, required_out_pattern= "b s n d", "b s n d"
215
- q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
216
- out = xops.memory_efficient_attention(q, k, v, scale=scale)
217
- out = rearrange_out(out, out_pattern, required_out_pattern, dims)
218
- return out
219
-
220
-
221
- def flex_attention(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, attn_mask=None, scale=None, score_mod=None):
222
- assert FLEX_ATTN_AVAILABLE, "Flex Attention is not available. Please upgrade torch to 2.5.0 or later."
223
- required_in_pattern, required_out_pattern = "b n s d", "b n s d"
224
- q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
225
- out = flex_attention_func(query=q, key=k, value=v, block_mask=attn_mask, scale=scale, score_mod=score_mod)
226
- out = rearrange_out(out, out_pattern, required_out_pattern, dims)
227
- return out
228
-
229
-
230
- def attention_forward(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, attn_mask=None, scale=None, is_causal=False, compatibility_mode=False, window_size=None, use_flex=False, score_mod=None):
231
- if compatibility_mode or (attn_mask is not None) or ATTENTION_IMPLEMENTATION == "torch":
232
- if use_flex or score_mod is not None:
233
- return flex_attention(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, attn_mask=attn_mask, scale=scale, score_mod=score_mod)
234
- elif window_size is None:
235
- return torch_sdpa(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, attn_mask=attn_mask, scale=scale, is_causal=is_causal)
236
- else:
237
- # Sliding Window Attention is not compatible with `is_causal` and `attn_mask`.
238
- assert is_causal == False and attn_mask is None
239
- return torch_sdpa_sliding_window(q, k, v, window_size, q_pattern, k_pattern, v_pattern, out_pattern, dims, scale=scale)
240
- elif ATTENTION_IMPLEMENTATION == "customized_fa_kernel":
241
- return flash_attention_customized_kernel(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, scale=scale, is_causal=is_causal, window_size=window_size)
242
- elif ATTENTION_IMPLEMENTATION == "flash_attention_4":
243
- return flash_attention_4(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, scale=scale, is_causal=is_causal, window_size=window_size)
244
- elif ATTENTION_IMPLEMENTATION == "flash_attention_3":
245
- return flash_attention_3(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, scale=scale, is_causal=is_causal, window_size=window_size)
246
- elif ATTENTION_IMPLEMENTATION == "flash_attention_2":
247
- return flash_attention_2(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, scale=scale, is_causal=is_causal, window_size=window_size)
248
- elif ATTENTION_IMPLEMENTATION == "sage_attention":
249
- if window_size is not None or is_causal: return attention_forward(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, attn_mask, scale, is_causal, compatibility_mode=True, window_size=window_size)
250
- return sage_attention(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, scale=scale)
251
- elif ATTENTION_IMPLEMENTATION == "xformers":
252
- if window_size is not None or is_causal: return attention_forward(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, attn_mask, scale, is_causal, compatibility_mode=True, window_size=window_size)
253
- return xformers_attention(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, scale=scale)
254
- else:
255
- raise NotImplementedError("No available attention implementation.")
44
+ kwargs = {"scale": scale}
45
+ if backend.supports_causal:
46
+ kwargs["is_causal"] = is_causal
47
+ if backend.supports_mask:
48
+ kwargs["attn_mask"] = attn_mask
49
+ if backend.supports_window:
50
+ kwargs["window_size"] = window_size
51
+ return backend.func(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, **kwargs)
@@ -0,0 +1,27 @@
1
+ import inspect
2
+
3
+ import torch
4
+ from einops import rearrange
5
+
6
+ try:
7
+ TORCH_SUPPORT_GQA = "enable_gqa" in inspect.signature(torch.nn.functional.scaled_dot_product_attention).parameters
8
+ except Exception:
9
+ TORCH_SUPPORT_GQA = False
10
+
11
+
12
+ def rearrange_qkv(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", required_in_pattern="b n s d", dims=None):
13
+ dims = {} if dims is None else dims
14
+ if q_pattern != required_in_pattern:
15
+ q = rearrange(q, f"{q_pattern} -> {required_in_pattern}", **dims)
16
+ if k_pattern != required_in_pattern:
17
+ k = rearrange(k, f"{k_pattern} -> {required_in_pattern}", **dims)
18
+ if v_pattern != required_in_pattern:
19
+ v = rearrange(v, f"{v_pattern} -> {required_in_pattern}", **dims)
20
+ return q, k, v
21
+
22
+
23
+ def rearrange_out(out: torch.Tensor, out_pattern="b n s d", required_out_pattern="b n s d", dims=None):
24
+ dims = {} if dims is None else dims
25
+ if out_pattern != required_out_pattern:
26
+ out = rearrange(out, f"{required_out_pattern} -> {out_pattern}", **dims)
27
+ return out
@@ -0,0 +1,118 @@
1
+ import os
2
+
3
+ from .common import rearrange_qkv, rearrange_out
4
+ from .registry import register_attention_implementation
5
+
6
+ if os.environ.get("DIFFSYNTH_FLASH_ATTN_KERNEL_REPO_ID") is not None:
7
+ try:
8
+ from kernels import get_kernel
9
+ kernel_module = get_kernel(os.environ.get("DIFFSYNTH_FLASH_ATTN_KERNEL_REPO_ID"), version=int(os.environ.get("DIFFSYNTH_FLASH_ATTN_KERNEL_VERSION")))
10
+ customized_flash_attn_func = kernel_module.flash_attn_func
11
+ CUSTOMIZED_FA_KERNEL_AVAILABLE = True
12
+ except Exception:
13
+ CUSTOMIZED_FA_KERNEL_AVAILABLE = False
14
+ else:
15
+ CUSTOMIZED_FA_KERNEL_AVAILABLE = False
16
+
17
+ try:
18
+ from flash_attn.cute import flash_attn_func as flash_attn_func_cute
19
+ FLASH_ATTN_4_AVAILABLE = True
20
+ except Exception:
21
+ FLASH_ATTN_4_AVAILABLE = False
22
+
23
+ try:
24
+ from flash_attn_3 import flash_attn_interface
25
+ FLASH_ATTN_3_AVAILABLE = True
26
+ except Exception:
27
+ FLASH_ATTN_3_AVAILABLE = False
28
+
29
+ try:
30
+ import flash_attn
31
+ FLASH_ATTN_2_AVAILABLE = hasattr(flash_attn, "flash_attn_func")
32
+ except Exception:
33
+ FLASH_ATTN_2_AVAILABLE = False
34
+
35
+
36
+ def _require(flag, name):
37
+ if not flag:
38
+ raise RuntimeError(f"{name} is not available.")
39
+
40
+
41
+ def flash_attention_4(q, k, v, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, scale=None, is_causal=False, window_size=None):
42
+ _require(FLASH_ATTN_4_AVAILABLE, "FlashAttention-4")
43
+ required_in_pattern, required_out_pattern = "b s n d", "b s n d"
44
+ q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
45
+ window_size = (window_size, window_size) if window_size is not None else (-1, -1)
46
+ out = flash_attn_func_cute(q, k, v, softmax_scale=scale, causal=is_causal, window_size=window_size)
47
+ if isinstance(out, tuple):
48
+ out = out[0]
49
+ out = rearrange_out(out, out_pattern, required_out_pattern, dims)
50
+ return out
51
+
52
+
53
+ def flash_attention_3(q, k, v, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, scale=None, is_causal=False, window_size=None):
54
+ _require(FLASH_ATTN_3_AVAILABLE, "FlashAttention-3")
55
+ required_in_pattern, required_out_pattern = "b s n d", "b s n d"
56
+ q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
57
+ window_size = (window_size, window_size) if window_size is not None else (-1, -1)
58
+ out = flash_attn_interface.flash_attn_func(q, k, v, softmax_scale=scale, causal=is_causal, window_size=window_size)
59
+ if isinstance(out, tuple):
60
+ out = out[0]
61
+ out = rearrange_out(out, out_pattern, required_out_pattern, dims)
62
+ return out
63
+
64
+
65
+ def flash_attention_2(q, k, v, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, scale=None, is_causal=False, window_size=None):
66
+ _require(FLASH_ATTN_2_AVAILABLE, "FlashAttention-2")
67
+ required_in_pattern, required_out_pattern = "b s n d", "b s n d"
68
+ q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
69
+ window_size = (window_size, window_size) if window_size is not None else (-1, -1)
70
+ out = flash_attn.flash_attn_func(q, k, v, softmax_scale=scale, causal=is_causal, window_size=window_size)
71
+ out = rearrange_out(out, out_pattern, required_out_pattern, dims)
72
+ return out
73
+
74
+
75
+ def flash_attention_customized_kernel(q, k, v, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, scale=None, is_causal=False, window_size=None):
76
+ _require(CUSTOMIZED_FA_KERNEL_AVAILABLE, "customized FlashAttention kernel")
77
+ required_in_pattern, required_out_pattern = "b s n d", "b s n d"
78
+ q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
79
+ window_size = (window_size, window_size) if window_size is not None else (-1, -1)
80
+ out = customized_flash_attn_func(q, k, v, softmax_scale=scale, causal=is_causal, window_size=window_size)
81
+ if isinstance(out, tuple):
82
+ out = out[0]
83
+ out = rearrange_out(out, out_pattern, required_out_pattern, dims)
84
+ return out
85
+
86
+
87
+ if CUSTOMIZED_FA_KERNEL_AVAILABLE:
88
+ register_attention_implementation(
89
+ "customized_fa_kernel",
90
+ flash_attention_customized_kernel,
91
+ supports_causal=True,
92
+ supports_window=True,
93
+ supports_gqa=True,
94
+ )
95
+ if FLASH_ATTN_4_AVAILABLE:
96
+ register_attention_implementation(
97
+ "flash_attention_4",
98
+ flash_attention_4,
99
+ supports_causal=True,
100
+ supports_window=True,
101
+ supports_gqa=True,
102
+ )
103
+ if FLASH_ATTN_3_AVAILABLE:
104
+ register_attention_implementation(
105
+ "flash_attention_3",
106
+ flash_attention_3,
107
+ supports_causal=True,
108
+ supports_window=True,
109
+ supports_gqa=True,
110
+ )
111
+ if FLASH_ATTN_2_AVAILABLE:
112
+ register_attention_implementation(
113
+ "flash_attention_2",
114
+ flash_attention_2,
115
+ supports_causal=True,
116
+ supports_window=True,
117
+ supports_gqa=True,
118
+ )
@@ -0,0 +1,28 @@
1
+ import torch
2
+
3
+ from .common import rearrange_qkv, rearrange_out
4
+
5
+ try:
6
+ from torch.nn.attention.flex_attention import flex_attention as _flex_attention_func
7
+ FLEX_ATTN_AVAILABLE = True
8
+ except Exception:
9
+ FLEX_ATTN_AVAILABLE = False
10
+
11
+ # Compile flex attention lazily
12
+ _compiled_flex_attention_func = None
13
+
14
+
15
+ def get_flex_attention_func():
16
+ global _compiled_flex_attention_func
17
+ if _compiled_flex_attention_func is None:
18
+ _compiled_flex_attention_func = torch.compile(_flex_attention_func)
19
+ return _compiled_flex_attention_func
20
+
21
+
22
+ def flex_attention(q, k, v, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, attn_mask=None, scale=None, score_mod=None):
23
+ assert FLEX_ATTN_AVAILABLE, "Flex Attention is not available. Please upgrade torch to 2.5.0 or later."
24
+ required_in_pattern, required_out_pattern = "b n s d", "b n s d"
25
+ q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
26
+ out = get_flex_attention_func()(query=q, key=k, value=v, block_mask=attn_mask, scale=scale, score_mod=score_mod)
27
+ out = rearrange_out(out, out_pattern, required_out_pattern, dims)
28
+ return out
@@ -0,0 +1,48 @@
1
+ from dataclasses import dataclass
2
+ from typing import Callable, Optional
3
+
4
+
5
+ @dataclass(frozen=True)
6
+ class AttentionBackend:
7
+ func: Callable
8
+ supports_causal: bool = False
9
+ supports_window: bool = False
10
+ supports_mask: bool = False
11
+ supports_gqa: bool = False
12
+
13
+
14
+ _ATTENTION_BACKENDS = {}
15
+
16
+
17
+ def register_attention_implementation(
18
+ name: str,
19
+ func: Optional[Callable] = None,
20
+ supports_causal: bool = False,
21
+ supports_window: bool = False,
22
+ supports_mask: bool = False,
23
+ supports_gqa: bool = False,
24
+ ):
25
+ key = name.strip().lower()
26
+ _ATTENTION_BACKENDS[key] = AttentionBackend(
27
+ func=func,
28
+ supports_causal=supports_causal,
29
+ supports_window=supports_window,
30
+ supports_mask=supports_mask,
31
+ supports_gqa=supports_gqa,
32
+ )
33
+
34
+
35
+ def get_attention_implementation(name: str) -> AttentionBackend:
36
+ key = name.strip().lower()
37
+ if key not in _ATTENTION_BACKENDS:
38
+ available = ", ".join(list_attention_implementations()) or "none"
39
+ raise ValueError(
40
+ f"Unknown attention implementation: {name!r}. "
41
+ f"Available implementations: {available}. "
42
+ f"Please check the environment variable `DIFFSYNTH_ATTENTION_IMPLEMENTATION`."
43
+ )
44
+ return _ATTENTION_BACKENDS[key]
45
+
46
+
47
+ def list_attention_implementations():
48
+ return sorted(_ATTENTION_BACKENDS)
@@ -0,0 +1,20 @@
1
+ from .common import rearrange_qkv, rearrange_out
2
+ from .registry import register_attention_implementation
3
+
4
+ try:
5
+ from sageattention import sageattn
6
+ SAGE_ATTN_AVAILABLE = True
7
+ except Exception:
8
+ SAGE_ATTN_AVAILABLE = False
9
+
10
+ def sage_attention(q, k, v, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, scale=None):
11
+ if not SAGE_ATTN_AVAILABLE:
12
+ raise RuntimeError("SageAttention is not available.")
13
+ required_in_pattern, required_out_pattern = "b n s d", "b n s d"
14
+ q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
15
+ out = sageattn(q, k, v, sm_scale=scale)
16
+ out = rearrange_out(out, out_pattern, required_out_pattern, dims)
17
+ return out
18
+
19
+ if SAGE_ATTN_AVAILABLE:
20
+ register_attention_implementation("sage_attention", sage_attention)
@@ -0,0 +1,82 @@
1
+ import torch
2
+ from einops import repeat
3
+
4
+ from .common import TORCH_SUPPORT_GQA, rearrange_qkv, rearrange_out
5
+ from .registry import register_attention_implementation
6
+
7
+
8
+ def torch_sdpa(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, attn_mask=None, scale=None, is_causal=False, window_size=None):
9
+ if window_size is not None:
10
+ if is_causal or attn_mask is not None:
11
+ raise ValueError("Sliding window attention does not support `is_causal=True` or `attn_mask`.")
12
+ return torch_sdpa_sliding_window(q, k, v, window_size, q_pattern, k_pattern, v_pattern, out_pattern, dims, scale=scale)
13
+
14
+ required_in_pattern, required_out_pattern = "b n s d", "b n s d"
15
+ q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
16
+ if q.shape[1] != k.shape[1] or q.shape[1] != v.shape[1]:
17
+ # Grouped Query Attention
18
+ if TORCH_SUPPORT_GQA:
19
+ out = torch.nn.functional.scaled_dot_product_attention(q, k, v, attn_mask, scale=scale, is_causal=is_causal, enable_gqa=True)
20
+ else:
21
+ # In low-version torch, `enable_gqa` is not supported.
22
+ k = repeat(k, "b n s d -> b (n m) s d", m=q.shape[1]//k.shape[1])
23
+ v = repeat(v, "b n s d -> b (n m) s d", m=q.shape[1]//v.shape[1])
24
+ out = torch.nn.functional.scaled_dot_product_attention(q, k, v, attn_mask, scale=scale, is_causal=is_causal)
25
+ else:
26
+ out = torch.nn.functional.scaled_dot_product_attention(q, k, v, attn_mask, scale=scale, is_causal=is_causal)
27
+ out = rearrange_out(out, out_pattern, required_out_pattern, dims)
28
+ return out
29
+
30
+
31
+ def torch_sdpa_sliding_window(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, sliding_window: int, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, scale=None):
32
+ required_in_pattern, required_out_pattern = "b n s d", "b n s d"
33
+ q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
34
+
35
+ B, N, S, D = q.shape
36
+ W = sliding_window
37
+ chunk_size = W
38
+ num_chunks = (S + chunk_size - 1) // chunk_size
39
+
40
+ output = torch.empty_like(q)
41
+ dtype = q.dtype
42
+ device = q.device
43
+ min_val = torch.finfo(dtype).min
44
+
45
+ for i in range(num_chunks):
46
+ q_start = i * chunk_size
47
+ q_end = min(q_start + chunk_size, S)
48
+ actual_chunk_size = q_end - q_start
49
+
50
+ kv_start = max(0, q_start - W)
51
+ kv_end = min(S, q_end + W)
52
+
53
+ q_chunk = q[:, :, q_start:q_end, :]
54
+ k_chunk = k[:, :, kv_start:kv_end, :]
55
+ v_chunk = v[:, :, kv_start:kv_end, :]
56
+
57
+ q_indices = torch.arange(q_start, q_end, device=device)
58
+ k_indices = torch.arange(kv_start, kv_end, device=device)
59
+ diff = q_indices.unsqueeze(1) - k_indices.unsqueeze(0)
60
+ valid = diff.abs() <= W
61
+
62
+ local_mask = torch.zeros(actual_chunk_size, kv_end - kv_start, dtype=dtype, device=device)
63
+ local_mask.masked_fill_(~valid, min_val)
64
+ local_mask = local_mask.unsqueeze(0).unsqueeze(0)
65
+
66
+ out_chunk = torch_sdpa(
67
+ q_chunk, k_chunk, v_chunk, attn_mask=local_mask, scale=scale
68
+ )
69
+ output[:, :, q_start:q_end, :] = out_chunk
70
+
71
+ output = rearrange_out(output, out_pattern, required_out_pattern, dims)
72
+ return output
73
+
74
+
75
+ register_attention_implementation(
76
+ "torch",
77
+ torch_sdpa,
78
+ supports_window=True,
79
+ supports_causal=True,
80
+ supports_mask=True,
81
+ supports_gqa=True,
82
+ )
@@ -0,0 +1,22 @@
1
+ from .common import rearrange_qkv, rearrange_out
2
+ from .registry import register_attention_implementation
3
+
4
+ try:
5
+ import xformers.ops as xops
6
+ XFORMERS_AVAILABLE = True
7
+ except Exception:
8
+ XFORMERS_AVAILABLE = False
9
+
10
+
11
+ def xformers_attention(q, k, v, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, scale=None):
12
+ if not XFORMERS_AVAILABLE:
13
+ raise RuntimeError("xformers is not available.")
14
+ required_in_pattern, required_out_pattern = "b s n d", "b s n d"
15
+ q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
16
+ out = xops.memory_efficient_attention(q, k, v, scale=scale)
17
+ out = rearrange_out(out, out_pattern, required_out_pattern, dims)
18
+ return out
19
+
20
+
21
+ if XFORMERS_AVAILABLE:
22
+ register_attention_implementation("xformers", xformers_attention)