lhallee commited on
Commit
ccf7228
·
verified ·
1 Parent(s): b9ac427

Upload vb_tri_attn_primitives.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. vb_tri_attn_primitives.py +411 -411
vb_tri_attn_primitives.py CHANGED
@@ -1,411 +1,411 @@
1
- # Copyright 2021 AlQuraishi Laboratory
2
- # Copyright 2021 DeepMind Technologies Limited
3
- #
4
- # Licensed under the Apache License, Version 2.0 (the "License");
5
- # you may not use this file except in compliance with the License.
6
- # You may obtain a copy of the License at
7
- #
8
- # http://www.apache.org/licenses/LICENSE-2.0
9
- #
10
- # Unless required by applicable law or agreed to in writing, software
11
- # distributed under the License is distributed on an "AS IS" BASIS,
12
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- # See the License for the specific language governing permissions and
14
- # limitations under the License.
15
-
16
- import math
17
- import importlib
18
- from typing import Callable, List, Optional, Tuple
19
-
20
- import torch
21
- from einops import rearrange
22
- from torch import nn
23
-
24
- from . import vb_layers_initialize as initialize
25
- from .vb_tri_attn_utils import (
26
- flatten_final_dims,
27
- permute_final_dims,
28
- )
29
-
30
-
31
- class Linear(nn.Linear):
32
- """
33
- A Linear layer with built-in nonstandard initializations. Called just
34
- like torch.nn.Linear.
35
-
36
- Implements the initializers in 1.11.4, plus some additional ones found
37
- in the code.
38
- """
39
-
40
- def __init__(
41
- self,
42
- in_dim: int,
43
- out_dim: int,
44
- bias: bool = True,
45
- init: str = "default",
46
- init_fn: Optional[Callable[[torch.Tensor, torch.Tensor], None]] = None,
47
- precision=None,
48
- ):
49
- """Initialize the linear layer.
50
-
51
- Parameters
52
- ----------
53
- in_dim : int
54
- The final dimension of inputs to the layer
55
- out_dim : int
56
- The final dimension of layer outputs
57
- bias : bool, default=True
58
- Whether to learn an additive bias
59
- init : str, default='default'
60
- The initializer to use. Choose from:
61
-
62
- - "default": LeCun fan-in truncated normal initialization
63
- - "relu": He initialization w/ truncated normal distribution
64
- - "glorot": Fan-average Glorot uniform initialization
65
- - "gating": Weights=0, Bias=1
66
- - "normal": Normal initialization with std=1/sqrt(fan_in)
67
- - "final": Weights=0, Bias=0
68
-
69
- Overridden by init_fn if the latter is not None.
70
- init_fn : callable, optional
71
- A custom initializer taking weight and bias as inputs.
72
- Overrides init if not None.
73
-
74
- """
75
- super().__init__(in_dim, out_dim, bias=bias)
76
-
77
- if bias:
78
- with torch.no_grad():
79
- self.bias.fill_(0)
80
-
81
- with torch.no_grad():
82
- if init_fn is not None:
83
- init_fn(self.weight, self.bias)
84
- else:
85
- if init == "default":
86
- initialize.lecun_normal_init_(self.weight)
87
- elif init == "relu":
88
- initialize.he_normal_init_(self.weight)
89
- elif init == "glorot":
90
- initialize.glorot_uniform_init_(self.weight)
91
- elif init == "gating":
92
- initialize.gating_init_(self.weight)
93
- if bias:
94
- self.bias.fill_(1.0)
95
- elif init == "normal":
96
- initialize.normal_init_(self.weight)
97
- elif init == "final":
98
- initialize.final_init_(self.weight)
99
- else:
100
- raise ValueError("Invalid init string.")
101
-
102
- self.precision = precision
103
-
104
- def forward(self, input: torch.Tensor) -> torch.Tensor:
105
- d = input.dtype
106
- if self.precision is not None:
107
- with torch.autocast("cuda", enabled=False):
108
- bias = (
109
- self.bias.to(dtype=self.precision)
110
- if self.bias is not None
111
- else None
112
- )
113
- return nn.functional.linear(
114
- input.to(dtype=self.precision),
115
- self.weight.to(dtype=self.precision),
116
- bias,
117
- ).to(dtype=d)
118
-
119
- if d is torch.bfloat16:
120
- with torch.autocast("cuda", enabled=False):
121
- bias = self.bias.to(dtype=d) if self.bias is not None else None
122
- return nn.functional.linear(input, self.weight.to(dtype=d), bias)
123
-
124
- return nn.functional.linear(input, self.weight, self.bias)
125
-
126
-
127
- class LayerNorm(nn.Module):
128
- def __init__(self, c_in, eps=1e-5):
129
- super(LayerNorm, self).__init__()
130
-
131
- self.c_in = (c_in,)
132
- self.eps = eps
133
-
134
- self.weight = nn.Parameter(torch.ones(c_in))
135
- self.bias = nn.Parameter(torch.zeros(c_in))
136
-
137
- def forward(self, x):
138
- d = x.dtype
139
- if d is torch.bfloat16:
140
- with torch.autocast("cuda", enabled=False):
141
- out = nn.functional.layer_norm(
142
- x,
143
- self.c_in,
144
- self.weight.to(dtype=d),
145
- self.bias.to(dtype=d),
146
- self.eps,
147
- )
148
- else:
149
- out = nn.functional.layer_norm(
150
- x,
151
- self.c_in,
152
- self.weight,
153
- self.bias,
154
- self.eps,
155
- )
156
-
157
- return out
158
-
159
-
160
- @torch.jit.ignore
161
- def softmax_no_cast(t: torch.Tensor, dim: int = -1) -> torch.Tensor:
162
- """
163
- Softmax, but without automatic casting to fp32 when the input is of
164
- type bfloat16
165
- """
166
- d = t.dtype
167
- if d is torch.bfloat16:
168
- with torch.autocast("cuda", enabled=False):
169
- s = torch.nn.functional.softmax(t, dim=dim)
170
- else:
171
- s = torch.nn.functional.softmax(t, dim=dim)
172
-
173
- return s
174
-
175
-
176
- # @torch.jit.script
177
- def _attention(
178
- query: torch.Tensor,
179
- key: torch.Tensor,
180
- value: torch.Tensor,
181
- biases: List[torch.Tensor],
182
- ) -> torch.Tensor:
183
- # [*, H, C_hidden, K]
184
- key = permute_final_dims(key, (1, 0))
185
-
186
- # [*, H, Q, K]
187
- a = torch.matmul(query, key)
188
-
189
- for b in biases:
190
- a += b
191
-
192
- a = softmax_no_cast(a, -1)
193
-
194
- # [*, H, Q, C_hidden]
195
- a = torch.matmul(a, value)
196
-
197
- return a
198
-
199
-
200
- @torch.compiler.disable
201
- def kernel_triangular_attn(q, k, v, tri_bias, mask, scale):
202
- triangle_module = importlib.import_module("cuequivariance_torch.primitives.triangle")
203
- triangle_attention = triangle_module.triangle_attention
204
- return triangle_attention(q, k, v, tri_bias, mask=mask, scale=scale)
205
-
206
-
207
- class Attention(nn.Module):
208
- """
209
- Standard multi-head attention using AlphaFold's default layer
210
- initialization. Allows multiple bias vectors.
211
- """
212
-
213
- def __init__(
214
- self,
215
- c_q: int,
216
- c_k: int,
217
- c_v: int,
218
- c_hidden: int,
219
- no_heads: int,
220
- gating: bool = True,
221
- ):
222
- """Initialize the attention layer.
223
-
224
- Parameters
225
- ----------
226
- c_q : int
227
- Input dimension of query data
228
- c_k : int
229
- Input dimension of key data
230
- c_v : int
231
- Input dimension of value data
232
- c_hidden : int
233
- Per-head hidden dimension
234
- no_heads : int
235
- Number of attention heads
236
- gating : bool, default=True
237
- Whether the output should be gated using query data
238
-
239
- """
240
- super().__init__()
241
-
242
- self.c_q = c_q
243
- self.c_k = c_k
244
- self.c_v = c_v
245
- self.c_hidden = c_hidden
246
- self.no_heads = no_heads
247
- self.gating = gating
248
-
249
- # DISCREPANCY: c_hidden is not the per-head channel dimension, as
250
- # stated in the supplement, but the overall channel dimension.
251
-
252
- self.linear_q = Linear(
253
- self.c_q, self.c_hidden * self.no_heads, bias=False, init="glorot"
254
- )
255
- self.linear_k = Linear(
256
- self.c_k, self.c_hidden * self.no_heads, bias=False, init="glorot"
257
- )
258
- self.linear_v = Linear(
259
- self.c_v, self.c_hidden * self.no_heads, bias=False, init="glorot"
260
- )
261
- self.linear_o = Linear(
262
- self.c_hidden * self.no_heads, self.c_q, bias=False, init="final"
263
- )
264
-
265
- self.linear_g = None
266
- if self.gating:
267
- self.linear_g = Linear(
268
- self.c_q, self.c_hidden * self.no_heads, bias=False, init="gating"
269
- )
270
-
271
- self.sigmoid = nn.Sigmoid()
272
-
273
- def _prep_qkv(
274
- self, q_x: torch.Tensor, kv_x: torch.Tensor, apply_scale: bool = True
275
- ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
276
- # [*, Q/K/V, H * C_hidden]
277
- q = self.linear_q(q_x)
278
- k = self.linear_k(kv_x)
279
- v = self.linear_v(kv_x)
280
-
281
- # [*, Q/K, H, C_hidden]
282
- q = q.view(q.shape[:-1] + (self.no_heads, -1))
283
- k = k.view(k.shape[:-1] + (self.no_heads, -1))
284
- v = v.view(v.shape[:-1] + (self.no_heads, -1))
285
-
286
- # [*, H, Q/K, C_hidden]
287
- q = q.transpose(-2, -3)
288
- k = k.transpose(-2, -3)
289
- v = v.transpose(-2, -3)
290
-
291
- if apply_scale:
292
- q /= math.sqrt(self.c_hidden)
293
-
294
- return q, k, v
295
-
296
- def _wrap_up(self, o: torch.Tensor, q_x: torch.Tensor) -> torch.Tensor:
297
- if self.linear_g is not None:
298
- g = self.sigmoid(self.linear_g(q_x))
299
-
300
- # [*, Q, H, C_hidden]
301
- g = g.view(g.shape[:-1] + (self.no_heads, -1))
302
- o = o * g
303
-
304
- # [*, Q, H * C_hidden]
305
- o = flatten_final_dims(o, 2)
306
-
307
- # [*, Q, C_q]
308
- o = self.linear_o(o)
309
-
310
- return o
311
-
312
- def forward(
313
- self,
314
- q_x: torch.Tensor,
315
- kv_x: torch.Tensor,
316
- tri_bias: torch.Tensor,
317
- mask_bias: torch.Tensor,
318
- mask: torch.Tensor,
319
- use_kernels: bool = False,
320
- ) -> torch.Tensor:
321
- """Compute attention.
322
-
323
- Parameters
324
- ----------
325
- q_x : torch.Tensor
326
- [*, Q, C_q] query data
327
- kv_x : torch.Tensor
328
- [*, K, C_k] key data
329
- tri_bias : torch.Tensor
330
- [*, H, Q, K] triangular bias
331
- mask_bias : torch.Tensor
332
- [*, H, Q, K] mask bias
333
- mask : torch.Tensor
334
- [*, Q, K] mask
335
- use_kernels : bool, default=False
336
- Whether to use optimized CUDA kernels
337
-
338
- Returns
339
- -------
340
- [*, Q, C_q] attention update
341
-
342
- """
343
- # Attention kernel applies scaling internally
344
- q, k, v = self._prep_qkv(
345
- q_x,
346
- kv_x,
347
- apply_scale=not use_kernels,
348
- )
349
-
350
- if use_kernels:
351
- scale = 1.0 / math.sqrt(self.c_hidden)
352
- o = kernel_triangular_attn(
353
- q,
354
- k,
355
- v,
356
- tri_bias=tri_bias,
357
- mask=mask.bool(),
358
- scale=scale,
359
- )
360
- o = o.transpose(-2, -3)
361
- else:
362
- biases = [mask_bias, tri_bias]
363
- o = _attention(q, k, v, biases)
364
- o = o.transpose(-2, -3)
365
-
366
- o = self._wrap_up(o, q_x)
367
-
368
- return o
369
-
370
-
371
- def _trifast_attn(q, k, v, biases):
372
- orig_n_dims = len(q.shape)
373
-
374
- if len(biases) != 2:
375
- raise ValueError(f"Trifast expects two bias terms, found {len(biases)}")
376
-
377
- mask, b = biases
378
-
379
- if len(b.shape) == 5:
380
- # Sometimes there is an extra batch dim -- why?
381
- b = b.squeeze(1)
382
-
383
- if orig_n_dims == 4:
384
- # add fake batch dim
385
- q = q.unsqueeze(0)
386
- k = k.unsqueeze(0)
387
- v = v.unsqueeze(0)
388
- # b = b.unsqueeze(0) not sure why this and only this has a batch dim?
389
- mask = mask.unsqueeze(0)
390
-
391
- if len(q.shape) != 5:
392
- raise ValueError(f"Trifast expects q/k/v to be 5D, found {len(q.shape)}")
393
-
394
- # Reorder q/k/v
395
- q = rearrange(q, "b i h j d -> b h i j d")
396
- k = rearrange(k, "b i h j d -> b h i j d")
397
- v = rearrange(v, "b i h j d -> b h i j d")
398
-
399
- # Make mask the right shape.
400
- mask = rearrange(mask, "b i () () j -> b i j").bool()
401
-
402
- # Delay import to here to avoid initializing cuda too early
403
- from trifast import triangle_attention
404
-
405
- o = triangle_attention(q, k, v, b, mask)
406
- o = rearrange(o, "b h i j d -> b i j h d")
407
-
408
- # Remove the batch dim if we added it.
409
- if orig_n_dims == 4:
410
- o = o.squeeze(0)
411
- return o
 
1
+ # Copyright 2021 AlQuraishi Laboratory
2
+ # Copyright 2021 DeepMind Technologies Limited
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import math
17
+ import importlib
18
+ from typing import Callable, List, Optional, Tuple
19
+
20
+ import torch
21
+ from einops import rearrange
22
+ from torch import nn
23
+
24
+ from . import vb_layers_initialize as initialize
25
+ from .vb_tri_attn_utils import (
26
+ flatten_final_dims,
27
+ permute_final_dims,
28
+ )
29
+
30
+
31
+ class Linear(nn.Linear):
32
+ """
33
+ A Linear layer with built-in nonstandard initializations. Called just
34
+ like torch.nn.Linear.
35
+
36
+ Implements the initializers in 1.11.4, plus some additional ones found
37
+ in the code.
38
+ """
39
+
40
+ def __init__(
41
+ self,
42
+ in_dim: int,
43
+ out_dim: int,
44
+ bias: bool = True,
45
+ init: str = "default",
46
+ init_fn: Optional[Callable[[torch.Tensor, torch.Tensor], None]] = None,
47
+ precision=None,
48
+ ):
49
+ """Initialize the linear layer.
50
+
51
+ Parameters
52
+ ----------
53
+ in_dim : int
54
+ The final dimension of inputs to the layer
55
+ out_dim : int
56
+ The final dimension of layer outputs
57
+ bias : bool, default=True
58
+ Whether to learn an additive bias
59
+ init : str, default='default'
60
+ The initializer to use. Choose from:
61
+
62
+ - "default": LeCun fan-in truncated normal initialization
63
+ - "relu": He initialization w/ truncated normal distribution
64
+ - "glorot": Fan-average Glorot uniform initialization
65
+ - "gating": Weights=0, Bias=1
66
+ - "normal": Normal initialization with std=1/sqrt(fan_in)
67
+ - "final": Weights=0, Bias=0
68
+
69
+ Overridden by init_fn if the latter is not None.
70
+ init_fn : callable, optional
71
+ A custom initializer taking weight and bias as inputs.
72
+ Overrides init if not None.
73
+
74
+ """
75
+ super().__init__(in_dim, out_dim, bias=bias)
76
+
77
+ if bias:
78
+ with torch.no_grad():
79
+ self.bias.fill_(0)
80
+
81
+ with torch.no_grad():
82
+ if init_fn is not None:
83
+ init_fn(self.weight, self.bias)
84
+ else:
85
+ if init == "default":
86
+ initialize.lecun_normal_init_(self.weight)
87
+ elif init == "relu":
88
+ initialize.he_normal_init_(self.weight)
89
+ elif init == "glorot":
90
+ initialize.glorot_uniform_init_(self.weight)
91
+ elif init == "gating":
92
+ initialize.gating_init_(self.weight)
93
+ if bias:
94
+ self.bias.fill_(1.0)
95
+ elif init == "normal":
96
+ initialize.normal_init_(self.weight)
97
+ elif init == "final":
98
+ initialize.final_init_(self.weight)
99
+ else:
100
+ raise ValueError("Invalid init string.")
101
+
102
+ self.precision = precision
103
+
104
+ def forward(self, input: torch.Tensor) -> torch.Tensor:
105
+ d = input.dtype
106
+ if self.precision is not None:
107
+ with torch.autocast("cuda", enabled=False):
108
+ bias = (
109
+ self.bias.to(dtype=self.precision)
110
+ if self.bias is not None
111
+ else None
112
+ )
113
+ return nn.functional.linear(
114
+ input.to(dtype=self.precision),
115
+ self.weight.to(dtype=self.precision),
116
+ bias,
117
+ ).to(dtype=d)
118
+
119
+ if d is torch.bfloat16:
120
+ with torch.autocast("cuda", enabled=False):
121
+ bias = self.bias.to(dtype=d) if self.bias is not None else None
122
+ return nn.functional.linear(input, self.weight.to(dtype=d), bias)
123
+
124
+ return nn.functional.linear(input, self.weight, self.bias)
125
+
126
+
127
+ class LayerNorm(nn.Module):
128
+ def __init__(self, c_in, eps=1e-5):
129
+ super(LayerNorm, self).__init__()
130
+
131
+ self.c_in = (c_in,)
132
+ self.eps = eps
133
+
134
+ self.weight = nn.Parameter(torch.ones(c_in))
135
+ self.bias = nn.Parameter(torch.zeros(c_in))
136
+
137
+ def forward(self, x):
138
+ d = x.dtype
139
+ if d is torch.bfloat16:
140
+ with torch.autocast("cuda", enabled=False):
141
+ out = nn.functional.layer_norm(
142
+ x,
143
+ self.c_in,
144
+ self.weight.to(dtype=d),
145
+ self.bias.to(dtype=d),
146
+ self.eps,
147
+ )
148
+ else:
149
+ out = nn.functional.layer_norm(
150
+ x,
151
+ self.c_in,
152
+ self.weight,
153
+ self.bias,
154
+ self.eps,
155
+ )
156
+
157
+ return out
158
+
159
+
160
+ @torch.jit.ignore
161
+ def softmax_no_cast(t: torch.Tensor, dim: int = -1) -> torch.Tensor:
162
+ """
163
+ Softmax, but without automatic casting to fp32 when the input is of
164
+ type bfloat16
165
+ """
166
+ d = t.dtype
167
+ if d is torch.bfloat16:
168
+ with torch.autocast("cuda", enabled=False):
169
+ s = torch.nn.functional.softmax(t, dim=dim)
170
+ else:
171
+ s = torch.nn.functional.softmax(t, dim=dim)
172
+
173
+ return s
174
+
175
+
176
+ # @torch.jit.script
177
+ def _attention(
178
+ query: torch.Tensor,
179
+ key: torch.Tensor,
180
+ value: torch.Tensor,
181
+ biases: List[torch.Tensor],
182
+ ) -> torch.Tensor:
183
+ # [*, H, C_hidden, K]
184
+ key = permute_final_dims(key, (1, 0))
185
+
186
+ # [*, H, Q, K]
187
+ a = torch.matmul(query, key)
188
+
189
+ for b in biases:
190
+ a += b
191
+
192
+ a = softmax_no_cast(a, -1)
193
+
194
+ # [*, H, Q, C_hidden]
195
+ a = torch.matmul(a, value)
196
+
197
+ return a
198
+
199
+
200
+ @torch.compiler.disable
201
+ def kernel_triangular_attn(q, k, v, tri_bias, mask, scale):
202
+ triangle_module = importlib.import_module("cuequivariance_torch.primitives.triangle")
203
+ triangle_attention = triangle_module.triangle_attention
204
+ return triangle_attention(q, k, v, tri_bias, mask=mask, scale=scale)
205
+
206
+
207
+ class Attention(nn.Module):
208
+ """
209
+ Standard multi-head attention using AlphaFold's default layer
210
+ initialization. Allows multiple bias vectors.
211
+ """
212
+
213
+ def __init__(
214
+ self,
215
+ c_q: int,
216
+ c_k: int,
217
+ c_v: int,
218
+ c_hidden: int,
219
+ no_heads: int,
220
+ gating: bool = True,
221
+ ):
222
+ """Initialize the attention layer.
223
+
224
+ Parameters
225
+ ----------
226
+ c_q : int
227
+ Input dimension of query data
228
+ c_k : int
229
+ Input dimension of key data
230
+ c_v : int
231
+ Input dimension of value data
232
+ c_hidden : int
233
+ Per-head hidden dimension
234
+ no_heads : int
235
+ Number of attention heads
236
+ gating : bool, default=True
237
+ Whether the output should be gated using query data
238
+
239
+ """
240
+ super().__init__()
241
+
242
+ self.c_q = c_q
243
+ self.c_k = c_k
244
+ self.c_v = c_v
245
+ self.c_hidden = c_hidden
246
+ self.no_heads = no_heads
247
+ self.gating = gating
248
+
249
+ # DISCREPANCY: c_hidden is not the per-head channel dimension, as
250
+ # stated in the supplement, but the overall channel dimension.
251
+
252
+ self.linear_q = Linear(
253
+ self.c_q, self.c_hidden * self.no_heads, bias=False, init="glorot"
254
+ )
255
+ self.linear_k = Linear(
256
+ self.c_k, self.c_hidden * self.no_heads, bias=False, init="glorot"
257
+ )
258
+ self.linear_v = Linear(
259
+ self.c_v, self.c_hidden * self.no_heads, bias=False, init="glorot"
260
+ )
261
+ self.linear_o = Linear(
262
+ self.c_hidden * self.no_heads, self.c_q, bias=False, init="final"
263
+ )
264
+
265
+ self.linear_g = None
266
+ if self.gating:
267
+ self.linear_g = Linear(
268
+ self.c_q, self.c_hidden * self.no_heads, bias=False, init="gating"
269
+ )
270
+
271
+ self.sigmoid = nn.Sigmoid()
272
+
273
+ def _prep_qkv(
274
+ self, q_x: torch.Tensor, kv_x: torch.Tensor, apply_scale: bool = True
275
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
276
+ # [*, Q/K/V, H * C_hidden]
277
+ q = self.linear_q(q_x)
278
+ k = self.linear_k(kv_x)
279
+ v = self.linear_v(kv_x)
280
+
281
+ # [*, Q/K, H, C_hidden]
282
+ q = q.view(q.shape[:-1] + (self.no_heads, -1))
283
+ k = k.view(k.shape[:-1] + (self.no_heads, -1))
284
+ v = v.view(v.shape[:-1] + (self.no_heads, -1))
285
+
286
+ # [*, H, Q/K, C_hidden]
287
+ q = q.transpose(-2, -3)
288
+ k = k.transpose(-2, -3)
289
+ v = v.transpose(-2, -3)
290
+
291
+ if apply_scale:
292
+ q /= math.sqrt(self.c_hidden)
293
+
294
+ return q, k, v
295
+
296
+ def _wrap_up(self, o: torch.Tensor, q_x: torch.Tensor) -> torch.Tensor:
297
+ if self.linear_g is not None:
298
+ g = self.sigmoid(self.linear_g(q_x))
299
+
300
+ # [*, Q, H, C_hidden]
301
+ g = g.view(g.shape[:-1] + (self.no_heads, -1))
302
+ o = o * g
303
+
304
+ # [*, Q, H * C_hidden]
305
+ o = flatten_final_dims(o, 2)
306
+
307
+ # [*, Q, C_q]
308
+ o = self.linear_o(o)
309
+
310
+ return o
311
+
312
+ def forward(
313
+ self,
314
+ q_x: torch.Tensor,
315
+ kv_x: torch.Tensor,
316
+ tri_bias: torch.Tensor,
317
+ mask_bias: torch.Tensor,
318
+ mask: torch.Tensor,
319
+ use_kernels: bool = False,
320
+ ) -> torch.Tensor:
321
+ """Compute attention.
322
+
323
+ Parameters
324
+ ----------
325
+ q_x : torch.Tensor
326
+ [*, Q, C_q] query data
327
+ kv_x : torch.Tensor
328
+ [*, K, C_k] key data
329
+ tri_bias : torch.Tensor
330
+ [*, H, Q, K] triangular bias
331
+ mask_bias : torch.Tensor
332
+ [*, H, Q, K] mask bias
333
+ mask : torch.Tensor
334
+ [*, Q, K] mask
335
+ use_kernels : bool, default=False
336
+ Whether to use optimized CUDA kernels
337
+
338
+ Returns
339
+ -------
340
+ [*, Q, C_q] attention update
341
+
342
+ """
343
+ # Attention kernel applies scaling internally
344
+ q, k, v = self._prep_qkv(
345
+ q_x,
346
+ kv_x,
347
+ apply_scale=not use_kernels,
348
+ )
349
+
350
+ if use_kernels:
351
+ scale = 1.0 / math.sqrt(self.c_hidden)
352
+ o = kernel_triangular_attn(
353
+ q,
354
+ k,
355
+ v,
356
+ tri_bias=tri_bias,
357
+ mask=mask.bool(),
358
+ scale=scale,
359
+ )
360
+ o = o.transpose(-2, -3)
361
+ else:
362
+ biases = [mask_bias, tri_bias]
363
+ o = _attention(q, k, v, biases)
364
+ o = o.transpose(-2, -3)
365
+
366
+ o = self._wrap_up(o, q_x)
367
+
368
+ return o
369
+
370
+
371
+ def _trifast_attn(q, k, v, biases):
372
+ orig_n_dims = len(q.shape)
373
+
374
+ if len(biases) != 2:
375
+ raise ValueError(f"Trifast expects two bias terms, found {len(biases)}")
376
+
377
+ mask, b = biases
378
+
379
+ if len(b.shape) == 5:
380
+ # Sometimes there is an extra batch dim -- why?
381
+ b = b.squeeze(1)
382
+
383
+ if orig_n_dims == 4:
384
+ # add fake batch dim
385
+ q = q.unsqueeze(0)
386
+ k = k.unsqueeze(0)
387
+ v = v.unsqueeze(0)
388
+ # b = b.unsqueeze(0) not sure why this and only this has a batch dim?
389
+ mask = mask.unsqueeze(0)
390
+
391
+ if len(q.shape) != 5:
392
+ raise ValueError(f"Trifast expects q/k/v to be 5D, found {len(q.shape)}")
393
+
394
+ # Reorder q/k/v
395
+ q = rearrange(q, "b i h j d -> b h i j d")
396
+ k = rearrange(k, "b i h j d -> b h i j d")
397
+ v = rearrange(v, "b i h j d -> b h i j d")
398
+
399
+ # Make mask the right shape.
400
+ mask = rearrange(mask, "b i () () j -> b i j").bool()
401
+
402
+ # Delay import to here to avoid initializing cuda too early
403
+ from trifast import triangle_attention
404
+
405
+ o = triangle_attention(q, k, v, b, mask)
406
+ o = rearrange(o, "b h i j d -> b i j h d")
407
+
408
+ # Remove the batch dim if we added it.
409
+ if orig_n_dims == 4:
410
+ o = o.squeeze(0)
411
+ return o