continuousintelligence 0.1.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.
- continuousintelligence/__init__.py +9 -0
- continuousintelligence/_data/NOTICE +16 -0
- continuousintelligence/_data/bike_sharing.csv +145 -0
- continuousintelligence/_example.py +20 -0
- continuousintelligence/_loader.py +140 -0
- continuousintelligence/_plot.py +158 -0
- continuousintelligence/_vendor/__init__.py +1 -0
- continuousintelligence/_vendor/mamba3/LICENSE +201 -0
- continuousintelligence/_vendor/mamba3/__init__.py +6 -0
- continuousintelligence/_vendor/mamba3/angle_dt.py +213 -0
- continuousintelligence/_vendor/mamba3/layernorm_gated.py +115 -0
- continuousintelligence/_vendor/mamba3/mamba3_mimo_fwd.py +483 -0
- continuousintelligence/_vendor/mamba3/mamba3_mimo_utils.py +112 -0
- continuousintelligence/_vendor/mamba3/utils.py +25 -0
- continuousintelligence/ci_proto/__init__.py +5 -0
- continuousintelligence/ci_proto/_calendar.py +153 -0
- continuousintelligence/ci_proto/_checkpoint.py +106 -0
- continuousintelligence/ci_proto/_cuda.py +135 -0
- continuousintelligence/ci_proto/_forecast.py +708 -0
- continuousintelligence/ci_proto/_groups.py +198 -0
- continuousintelligence/ci_proto/_mamba.py +190 -0
- continuousintelligence/ci_proto/_model.py +489 -0
- continuousintelligence-0.1.0.dist-info/METADATA +29 -0
- continuousintelligence-0.1.0.dist-info/RECORD +30 -0
- continuousintelligence-0.1.0.dist-info/WHEEL +5 -0
- continuousintelligence-0.1.0.dist-info/licenses/LICENCE +95 -0
- continuousintelligence-0.1.0.dist-info/licenses/NOTICE +33 -0
- continuousintelligence-0.1.0.dist-info/licenses/src/continuousintelligence/_data/NOTICE +16 -0
- continuousintelligence-0.1.0.dist-info/licenses/src/continuousintelligence/_vendor/mamba3/LICENSE +201 -0
- continuousintelligence-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tilelang implementation of Mamba3 forward kernel,
|
|
3
|
+
with MIMO support.
|
|
4
|
+
|
|
5
|
+
Copyright (c) 2026, Dao AI Lab, Goombalab
|
|
6
|
+
|
|
7
|
+
Licensed under Apache-2.0; see LICENSE.
|
|
8
|
+
Modified by Continuous Intelligence: local imports and cached TileLang symbols.
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import torch
|
|
13
|
+
import tilelang
|
|
14
|
+
import tilelang.language as T
|
|
15
|
+
|
|
16
|
+
from functools import cache
|
|
17
|
+
|
|
18
|
+
_dynamic = cache(T.dynamic)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# NOTE: Uncomment the following to autotune:
|
|
22
|
+
# def get_configs():
|
|
23
|
+
# iter_params = dict(num_stages=[0, 1, 2, 3], threads=[128, 256, 512])
|
|
24
|
+
# # iter_params = dict(num_stages=[2], threads=[128])
|
|
25
|
+
# return [dict(zip(iter_params, values)) for values in itertools.product(*iter_params.values())]
|
|
26
|
+
|
|
27
|
+
# @autotune(
|
|
28
|
+
# configs=get_configs(),
|
|
29
|
+
# warmup=3,
|
|
30
|
+
# rep=20,
|
|
31
|
+
# )
|
|
32
|
+
@tilelang.jit(
|
|
33
|
+
out_idx=[],
|
|
34
|
+
pass_configs={
|
|
35
|
+
tilelang.PassConfigKey.TL_DISABLE_TMA_LOWER: True,
|
|
36
|
+
tilelang.PassConfigKey.TL_DISABLE_WARP_SPECIALIZED: True,
|
|
37
|
+
tilelang.PassConfigKey.TL_ENABLE_FAST_MATH: True,
|
|
38
|
+
})
|
|
39
|
+
def mamba_mimo_fwd(
|
|
40
|
+
B,
|
|
41
|
+
S,
|
|
42
|
+
H,
|
|
43
|
+
G,
|
|
44
|
+
N,
|
|
45
|
+
P,
|
|
46
|
+
R,
|
|
47
|
+
hasZ,
|
|
48
|
+
hasD,
|
|
49
|
+
reduceO,
|
|
50
|
+
return_final_state=False,
|
|
51
|
+
chunk_size: int = 16,
|
|
52
|
+
rotary_dim_divisor = 4,
|
|
53
|
+
dtype: str = 'bfloat16',
|
|
54
|
+
threads: int = 128,
|
|
55
|
+
num_stages: int = 0,
|
|
56
|
+
) -> torch.Tensor:
|
|
57
|
+
|
|
58
|
+
accum_dtype = 'float32'
|
|
59
|
+
|
|
60
|
+
nchunks = tilelang.cdiv(S, chunk_size)
|
|
61
|
+
tail_len = S % chunk_size
|
|
62
|
+
fused_chunk_size = chunk_size * R
|
|
63
|
+
|
|
64
|
+
if reduceO:
|
|
65
|
+
O_shape = (B, S, H, P)
|
|
66
|
+
else:
|
|
67
|
+
O_shape = (B, S, R, H, P)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@T.prim_func
|
|
71
|
+
def mamba_mimo_fwd_kernel(
|
|
72
|
+
Q: T.Tensor([B, S, R, G, N], dtype), # type: ignore
|
|
73
|
+
K: T.Tensor([B, S, R, G, N], dtype), # type: ignore
|
|
74
|
+
V: T.Tensor([B, S, H, P], dtype), # type: ignore
|
|
75
|
+
O: T.Tensor(O_shape, dtype), # type: ignore
|
|
76
|
+
Q_BIAS: T.Tensor([H, R, N], T.float32), # type: ignore
|
|
77
|
+
K_BIAS: T.Tensor([H, R, N], T.float32), # type: ignore
|
|
78
|
+
MIMO_V: T.Tensor([H, R, P], T.float32), # type: ignore
|
|
79
|
+
MIMO_O: T.Tensor([H, R, P], T.float32), # type: ignore
|
|
80
|
+
Z: T.Tensor([B, S, H, P], dtype), # type: ignore
|
|
81
|
+
D: T.Tensor([H], T.float32), # type: ignore
|
|
82
|
+
MIMO_Z: T.Tensor([H, R, P], T.float32), # type: ignore
|
|
83
|
+
ANGLES: T.Tensor([B, S, H, N//rotary_dim_divisor], T.float32), # type: ignore
|
|
84
|
+
DA_CS: T.Tensor([B, H, S], T.float32), # type: ignore
|
|
85
|
+
DA_CS_REV: T.Tensor([B, H, S], T.float32), # type: ignore
|
|
86
|
+
DT: T.Tensor([B, H, S], T.float32), # type: ignore
|
|
87
|
+
TRAP: T.Tensor([B, H, S], dtype), # type: ignore
|
|
88
|
+
SEGSUM: T.Tensor([B, H, nchunks, chunk_size, chunk_size], T.float32), # type: ignore
|
|
89
|
+
|
|
90
|
+
FINAL_STATE: T.Tensor([B, H, N, P], T.float32), # type: ignore
|
|
91
|
+
FINAL_K: T.Tensor([B, R, H, N], dtype) # type: ignore
|
|
92
|
+
):
|
|
93
|
+
"""
|
|
94
|
+
Overview:
|
|
95
|
+
Fused chunked forward pass that combines MIMO projections with recurrent state updates.
|
|
96
|
+
Computes interchunk and intrachunk contributions with optional D and Z paths,
|
|
97
|
+
then writes output activations.
|
|
98
|
+
|
|
99
|
+
Inputs:
|
|
100
|
+
- Activations: Q, K, V.
|
|
101
|
+
- Projection parameters/biases: MIMO_V (Psi), MIMO_O (Phi), optional MIMO_Z (Zeta), ANGLES,
|
|
102
|
+
and Q_BIAS/K_BIAS.
|
|
103
|
+
- Optional modifiers: Z, and D.
|
|
104
|
+
- Discretization tensors: DA_CS, DA_CS_REV, DT, TRAP, and SEGSUM.
|
|
105
|
+
|
|
106
|
+
Outputs:
|
|
107
|
+
- O: fused forward output activations.
|
|
108
|
+
- FINAL_STATE: final recurrent states (if return_final_state is True).
|
|
109
|
+
- FINAL_K: final K tensor (if return_state is True, for use in decode)
|
|
110
|
+
|
|
111
|
+
Notation:
|
|
112
|
+
- Psi: MIMO X projection.
|
|
113
|
+
- Phi: MIMO O projection.
|
|
114
|
+
- Zeta: MIMO Z projection.
|
|
115
|
+
- Trap: convex-combination modulator used in exponential-trapezoidal discretization.
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
with T.Kernel(H, B, threads=threads) as (i_h, i_b):
|
|
119
|
+
# --- Kernel Setup ---
|
|
120
|
+
# GQA support: map V head to Q/K head
|
|
121
|
+
i_h_qk = i_h // (H // G)
|
|
122
|
+
|
|
123
|
+
# --- Buffer Allocation ---
|
|
124
|
+
q_shared = T.alloc_shared([fused_chunk_size, N], dtype)
|
|
125
|
+
k_shared = T.alloc_shared([fused_chunk_size, N], dtype)
|
|
126
|
+
q_bias_frag = T.alloc_fragment([R, N], dtype)
|
|
127
|
+
k_bias_frag = T.alloc_fragment([R, N], dtype)
|
|
128
|
+
|
|
129
|
+
angles_shared = T.alloc_shared([chunk_size, N], dtype)
|
|
130
|
+
|
|
131
|
+
PsiV_shared = T.alloc_shared([fused_chunk_size, P], dtype)
|
|
132
|
+
qs_shared = T.alloc_shared([fused_chunk_size, P], dtype)
|
|
133
|
+
o_shared = T.alloc_shared([chunk_size, P], dtype)
|
|
134
|
+
v_shared = T.alloc_shared([chunk_size, P], dtype)
|
|
135
|
+
states_accum_cast_shared = T.alloc_shared([N, P], dtype)
|
|
136
|
+
qk_intrachunk_shared = T.alloc_shared([fused_chunk_size, fused_chunk_size], dtype)
|
|
137
|
+
qk_dot_full_shared = T.alloc_shared([fused_chunk_size, fused_chunk_size], dtype)
|
|
138
|
+
|
|
139
|
+
# --- Swizzling Annotation ---
|
|
140
|
+
T.annotate_layout({
|
|
141
|
+
q_shared: tilelang.layout.make_swizzled_layout(q_shared),
|
|
142
|
+
k_shared: tilelang.layout.make_swizzled_layout(k_shared),
|
|
143
|
+
v_shared: tilelang.layout.make_swizzled_layout(v_shared),
|
|
144
|
+
|
|
145
|
+
angles_shared: tilelang.layout.make_swizzled_layout(angles_shared),
|
|
146
|
+
|
|
147
|
+
PsiV_shared: tilelang.layout.make_swizzled_layout(PsiV_shared),
|
|
148
|
+
qs_shared: tilelang.layout.make_swizzled_layout(qs_shared),
|
|
149
|
+
o_shared: tilelang.layout.make_swizzled_layout(o_shared),
|
|
150
|
+
states_accum_cast_shared: tilelang.layout.make_swizzled_layout(states_accum_cast_shared),
|
|
151
|
+
qk_dot_full_shared: tilelang.layout.make_swizzled_layout(qk_dot_full_shared),
|
|
152
|
+
qk_intrachunk_shared: tilelang.layout.make_swizzled_layout(qk_intrachunk_shared),
|
|
153
|
+
})
|
|
154
|
+
T.use_swizzle(10, "row")
|
|
155
|
+
|
|
156
|
+
T.no_set_max_nreg()
|
|
157
|
+
|
|
158
|
+
# --- Per-Head Constants / Running State ---
|
|
159
|
+
states_frag = T.alloc_fragment([N, P], accum_dtype)
|
|
160
|
+
T.clear(states_frag)
|
|
161
|
+
|
|
162
|
+
phi_frag_intrachunk = T.alloc_fragment([R, P], dtype=dtype)
|
|
163
|
+
if reduceO:
|
|
164
|
+
T.copy(MIMO_O[i_h, :, :], phi_frag_intrachunk)
|
|
165
|
+
Psi_frag = T.alloc_fragment([R, P], dtype)
|
|
166
|
+
T.copy(MIMO_V[i_h, :, :], Psi_frag)
|
|
167
|
+
|
|
168
|
+
T.copy(Q_BIAS[i_h, :, :], q_bias_frag)
|
|
169
|
+
T.copy(K_BIAS[i_h, :, :], k_bias_frag)
|
|
170
|
+
|
|
171
|
+
# --- Chunk Loop ---
|
|
172
|
+
for i in T.Pipelined(0, nchunks, num_stages=num_stages):
|
|
173
|
+
chunk_start = i * chunk_size
|
|
174
|
+
|
|
175
|
+
# --- Discretization Factors (Shifted Gamma + Trap Scale) ---
|
|
176
|
+
trap_shifted_frag = T.alloc_fragment([chunk_size], T.float32)
|
|
177
|
+
T.copy(TRAP[i_b, i_h, chunk_start+1: chunk_start+chunk_size+1], trap_shifted_frag)
|
|
178
|
+
dt_shifted_frag = T.alloc_fragment([chunk_size], dtype)
|
|
179
|
+
T.copy(DT[i_b, i_h, chunk_start+1: chunk_start+chunk_size+1], dt_shifted_frag)
|
|
180
|
+
shifted_gamma_frag = T.alloc_fragment([chunk_size], dtype)
|
|
181
|
+
for cs in T.Parallel(chunk_size):
|
|
182
|
+
shifted_gamma_frag[cs] = T.if_then_else(chunk_start + cs < (S - 1),
|
|
183
|
+
dt_shifted_frag[cs] * T.sigmoid(-trap_shifted_frag[cs]),
|
|
184
|
+
0.0)
|
|
185
|
+
shifted_gamma_shared = T.alloc_shared([chunk_size], dtype)
|
|
186
|
+
T.copy(shifted_gamma_frag, shifted_gamma_shared)
|
|
187
|
+
|
|
188
|
+
trap_frag = T.alloc_fragment([chunk_size], T.float32)
|
|
189
|
+
T.copy(TRAP[i_b, i_h, chunk_start: chunk_start+chunk_size], trap_frag)
|
|
190
|
+
dt_frag = T.alloc_fragment([chunk_size], dtype)
|
|
191
|
+
T.copy(DT[i_b, i_h, chunk_start: chunk_start+chunk_size], dt_frag)
|
|
192
|
+
gamma_frag = T.alloc_fragment([chunk_size], T.float32)
|
|
193
|
+
for cs in T.Parallel(chunk_size):
|
|
194
|
+
gamma_frag[cs] = dt_frag[cs] * T.sigmoid(trap_frag[cs])
|
|
195
|
+
trap_scale_frag = T.alloc_fragment([chunk_size], dtype)
|
|
196
|
+
for cs in T.Parallel(chunk_size):
|
|
197
|
+
trap_scale_frag[cs] = gamma_frag[cs] + shifted_gamma_shared[cs]
|
|
198
|
+
trap_scale_shared = T.alloc_shared([chunk_size], dtype)
|
|
199
|
+
T.copy(trap_scale_frag, trap_scale_shared)
|
|
200
|
+
|
|
201
|
+
# --- Up-Project V and Prepare Biased Q/K ---
|
|
202
|
+
PsiV_frag = T.alloc_fragment([chunk_size, R, P], dtype)
|
|
203
|
+
for cs, p in T.Parallel(chunk_size, P):
|
|
204
|
+
v_shared[cs, p] = V[i_b, chunk_start+cs, i_h, p]
|
|
205
|
+
for cs, r, p in T.Parallel(chunk_size, R, P):
|
|
206
|
+
PsiV_frag[cs, r, p] = v_shared[cs, p] * Psi_frag[r, p]
|
|
207
|
+
PsiV_reshaped_frag = T.view(PsiV_frag, shape=[fused_chunk_size, P])
|
|
208
|
+
T.copy(PsiV_reshaped_frag, PsiV_shared)
|
|
209
|
+
|
|
210
|
+
q_frag = T.alloc_fragment([chunk_size, R, N], dtype)
|
|
211
|
+
T.copy(Q[i_b, chunk_start:chunk_start+chunk_size, :, i_h_qk, :], q_frag)
|
|
212
|
+
for cs, r, n in T.Parallel(chunk_size, R, N):
|
|
213
|
+
q_frag[cs, r, n] += q_bias_frag[r, n]
|
|
214
|
+
T.copy(T.view(q_frag, shape=[fused_chunk_size, N]), q_shared)
|
|
215
|
+
|
|
216
|
+
k_frag = T.alloc_fragment([chunk_size, R, N], dtype)
|
|
217
|
+
T.copy(K[i_b, chunk_start:chunk_start+chunk_size, :, i_h_qk, :], k_frag)
|
|
218
|
+
for cs, r, n in T.Parallel(chunk_size, R, N):
|
|
219
|
+
k_frag[cs, r, n] += k_bias_frag[r, n]
|
|
220
|
+
T.copy(T.view(k_frag, shape=[fused_chunk_size, N]), k_shared)
|
|
221
|
+
|
|
222
|
+
# --- Cache Diagonal qk_dot Path ---
|
|
223
|
+
# Keep full qk_dot in shared memory because we reuse same-step R x R blocks later.
|
|
224
|
+
qk_dot_frag = T.alloc_fragment([fused_chunk_size, fused_chunk_size], dtype=accum_dtype)
|
|
225
|
+
T.gemm(q_shared, k_shared, qk_dot_frag, transpose_B=True, clear_accum=True)
|
|
226
|
+
T.copy(qk_dot_frag, qk_dot_full_shared)
|
|
227
|
+
# Option B: extremely slow
|
|
228
|
+
# qk_dot_frag = T.alloc_fragment([chunk_size, R, R], dtype=accum_dtype)
|
|
229
|
+
# T.clear(qk_dot_frag)
|
|
230
|
+
# for cs, r_out, r_in in T.Parallel(chunk_size, R, R):
|
|
231
|
+
# for n in T.serial(N):
|
|
232
|
+
# qk_dot_frag[cs, r_out, r_in] += (
|
|
233
|
+
# q_frag[cs, r_out, n] * k_frag[cs, r_in, n]
|
|
234
|
+
# )
|
|
235
|
+
# T.copy(T.view(qk_dot_frag, shape=[fused_chunk_size, R]), qk_dot_shared)
|
|
236
|
+
# NOTE ("option C"): The following fails Tilelang compilation:
|
|
237
|
+
# qk_predot_frag = T.alloc_fragment([chunk_size, R, R, N], dtype)
|
|
238
|
+
# for cs, r_out, r_in, n in T.Parallel(chunk_size, R, R, N):
|
|
239
|
+
# qk_predot_frag[cs, r_out, r_in, n] = q_frag[cs, r_out, n] * k_frag[cs, r_in, n]
|
|
240
|
+
# qk_dot_frag = T.alloc_fragment([chunk_size, R, R], dtype)
|
|
241
|
+
# T.reduce_sum(qk_predot_frag, qk_dot_frag, dim=-1, clear=True)
|
|
242
|
+
# T.copy(T.view(qk_dot_frag, shape=[fused_chunk_size, R]), qk_dot_shared)
|
|
243
|
+
|
|
244
|
+
# --- Rotary Q + Interchunk Contribution ---
|
|
245
|
+
q_first_half_frag = T.alloc_fragment([chunk_size, R, N//rotary_dim_divisor], dtype)
|
|
246
|
+
q_second_half_frag = T.alloc_fragment([chunk_size, R, N//rotary_dim_divisor], dtype)
|
|
247
|
+
|
|
248
|
+
for cs, r, n in T.Parallel(chunk_size, R, N//rotary_dim_divisor):
|
|
249
|
+
q_first_half_frag[cs, r, n] = q_shared[cs*R + r, n]
|
|
250
|
+
q_second_half_frag[cs, r, n] = q_shared[cs*R + r, N//2 + n]
|
|
251
|
+
|
|
252
|
+
# NOTE: angles are casted to fp32 for numerical stability
|
|
253
|
+
angles_frag = T.alloc_fragment([chunk_size, N//rotary_dim_divisor], T.float32)
|
|
254
|
+
T.copy(ANGLES[i_b, chunk_start:chunk_start+chunk_size, i_h, :], angles_frag)
|
|
255
|
+
|
|
256
|
+
for cs, r, n in T.Parallel(chunk_size, R, N//rotary_dim_divisor):
|
|
257
|
+
q_shared[cs*R + r, n] = T.cos(angles_frag[cs, n]) * q_first_half_frag[cs, r, n] - T.sin(angles_frag[cs, n]) * q_second_half_frag[cs, r, n]
|
|
258
|
+
q_shared[cs*R + r, N//2 + n] = T.sin(angles_frag[cs, n]) * q_first_half_frag[cs, r, n] + T.cos(angles_frag[cs, n]) * q_second_half_frag[cs, r, n]
|
|
259
|
+
|
|
260
|
+
o_mimo_accum_frag = T.alloc_fragment([fused_chunk_size, P], dtype=accum_dtype)
|
|
261
|
+
T.copy(states_frag, states_accum_cast_shared)
|
|
262
|
+
T.gemm(q_shared, states_accum_cast_shared, o_mimo_accum_frag, clear_accum=True)
|
|
263
|
+
|
|
264
|
+
# --- Rotary K + Trap Scaling + Intrachunk Contribution ---
|
|
265
|
+
k_first_half_frag = T.alloc_fragment([chunk_size, R, N//rotary_dim_divisor], dtype)
|
|
266
|
+
k_second_half_frag = T.alloc_fragment([chunk_size, R, N//rotary_dim_divisor], dtype)
|
|
267
|
+
|
|
268
|
+
for cs, r, n in T.Parallel(chunk_size, R, N//rotary_dim_divisor):
|
|
269
|
+
k_first_half_frag[cs, r, n] = k_shared[cs*R + r, n]
|
|
270
|
+
k_second_half_frag[cs, r, n] = k_shared[cs*R + r, N//2 + n]
|
|
271
|
+
|
|
272
|
+
for cs, r, n in T.Parallel(chunk_size, R, N//rotary_dim_divisor):
|
|
273
|
+
k_shared[cs*R + r, n] = T.cos(angles_frag[cs, n]) * k_first_half_frag[cs, r, n] - T.sin(angles_frag[cs, n]) * k_second_half_frag[cs, r, n]
|
|
274
|
+
k_shared[cs*R + r, N//2 + n] = T.sin(angles_frag[cs, n]) * k_first_half_frag[cs, r, n] + T.cos(angles_frag[cs, n]) * k_second_half_frag[cs, r, n]
|
|
275
|
+
|
|
276
|
+
if i == nchunks - 1 and return_final_state:
|
|
277
|
+
seq_boundary = T.min(chunk_start + chunk_size, S) - chunk_start
|
|
278
|
+
for csr, n in T.Parallel(fused_chunk_size, N):
|
|
279
|
+
if csr >= (seq_boundary - 1) * R and csr < seq_boundary * R: # Only copy the last chunk's R rows to FINAL_K
|
|
280
|
+
FINAL_K[i_b, csr % R, i_h, n] = k_shared[csr, n]
|
|
281
|
+
|
|
282
|
+
k_trap_scaled_frag = T.alloc_fragment([fused_chunk_size, N], dtype)
|
|
283
|
+
T.copy(k_shared, k_trap_scaled_frag)
|
|
284
|
+
for csr, n in T.Parallel(fused_chunk_size, N):
|
|
285
|
+
k_trap_scaled_frag[csr, n] *= trap_scale_shared[csr//R]
|
|
286
|
+
T.copy(k_trap_scaled_frag, k_shared)
|
|
287
|
+
|
|
288
|
+
qk_intrachunk_frag = T.alloc_fragment([fused_chunk_size, fused_chunk_size], dtype=accum_dtype)
|
|
289
|
+
T.gemm(q_shared, k_shared, qk_intrachunk_frag, transpose_B=True, clear_accum=True)
|
|
290
|
+
|
|
291
|
+
# Strictly causal mask over chunk steps (exclude same-step diagonal).
|
|
292
|
+
da_cs__or__exp_da_cs_shared = T.alloc_shared([chunk_size], T.float32)
|
|
293
|
+
T.copy(DA_CS[i_b, i_h, chunk_start:chunk_start+chunk_size], da_cs__or__exp_da_cs_shared)
|
|
294
|
+
qk_intrachunk_masked_frag = T.alloc_fragment([fused_chunk_size, fused_chunk_size], dtype=dtype)
|
|
295
|
+
for csr_i, csr_j in T.Parallel(fused_chunk_size, fused_chunk_size):
|
|
296
|
+
qk_intrachunk_masked_frag[csr_i, csr_j] = T.if_then_else(
|
|
297
|
+
csr_i//R > csr_j//R, # NOTE: we do indeed want to exclude the diagonal
|
|
298
|
+
qk_intrachunk_frag[csr_i, csr_j]
|
|
299
|
+
* T.exp(SEGSUM[i_b, i_h, i, csr_i//R, csr_j//R]),
|
|
300
|
+
0.0
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
# Exponentiate da_cs__or__exp_da_cs_shared so that later usage does not have to:
|
|
304
|
+
for cs in T.Parallel(chunk_size):
|
|
305
|
+
da_cs__or__exp_da_cs_shared[cs] = T.exp(da_cs__or__exp_da_cs_shared[cs])
|
|
306
|
+
|
|
307
|
+
exp_da_cs_frag = T.alloc_fragment([chunk_size], dtype=T.float32)
|
|
308
|
+
T.copy(da_cs__or__exp_da_cs_shared, exp_da_cs_frag)
|
|
309
|
+
for csr, p in T.Parallel(fused_chunk_size, P):
|
|
310
|
+
o_mimo_accum_frag[csr, p] *= exp_da_cs_frag[csr//R]
|
|
311
|
+
|
|
312
|
+
# NOTE: if we gemm with qk_intrachunk_masked_frag the compiler will
|
|
313
|
+
# error with layout issue if threads != 128:
|
|
314
|
+
# Copy via shared memory to satisfy layout constraints before GEMM.
|
|
315
|
+
T.copy(qk_intrachunk_masked_frag, qk_intrachunk_shared)
|
|
316
|
+
# Adding the two intermediate outputs together (interchunk += intrachunk)
|
|
317
|
+
T.gemm(qk_intrachunk_shared, PsiV_shared, o_mimo_accum_frag, clear_accum=False)
|
|
318
|
+
|
|
319
|
+
# --- Add Diagonal Terms (qk_dot and optional D) ---
|
|
320
|
+
qkdot_psiv_frag = T.alloc_fragment([chunk_size, R, P], dtype=dtype)
|
|
321
|
+
T.clear(qkdot_psiv_frag)
|
|
322
|
+
for cs, r_out, p in T.Parallel(chunk_size, R, P):
|
|
323
|
+
for r_in in T.serial(R):
|
|
324
|
+
qkdot_psiv_frag[cs, r_out, p] += qk_dot_full_shared[cs * R + r_out, cs * R + r_in] * PsiV_shared[cs * R + r_in, p]
|
|
325
|
+
qkdot_psiv_frag[cs, r_out, p] *= gamma_frag[cs] # Apply shifted gamma
|
|
326
|
+
|
|
327
|
+
if hasD:
|
|
328
|
+
PsiV_D_frag = T.alloc_fragment([chunk_size, R, P], T.float32)
|
|
329
|
+
for cs, r, p in T.Parallel(chunk_size, R, P):
|
|
330
|
+
PsiV_D_frag[cs, r, p] = PsiV_shared[cs * R + r, p]
|
|
331
|
+
D_var = T.alloc_var(T.float32)
|
|
332
|
+
T.copy(D[i_h], D_var)
|
|
333
|
+
for cs, r_out, p in T.Parallel(chunk_size, R, P):
|
|
334
|
+
qkdot_psiv_frag[cs, r_out, p] += D_var * PsiV_D_frag[cs, r_out, p]
|
|
335
|
+
qkdot_psiv_reshaped_frag = T.view(qkdot_psiv_frag, shape=[fused_chunk_size, P])
|
|
336
|
+
for csr, p in T.Parallel(fused_chunk_size, P):
|
|
337
|
+
o_mimo_accum_frag[csr, p] += qkdot_psiv_reshaped_frag[csr, p]
|
|
338
|
+
|
|
339
|
+
# --- Optional Z Gating + Down-Projection ---
|
|
340
|
+
if reduceO:
|
|
341
|
+
if hasZ:
|
|
342
|
+
z_frag = T.alloc_fragment([chunk_size, P], dtype)
|
|
343
|
+
T.copy(Z[i_b, chunk_start:chunk_start+chunk_size, i_h, :], z_frag)
|
|
344
|
+
z_expanded_frag = T.alloc_fragment([chunk_size, R, P], dtype)
|
|
345
|
+
for cs, r, p in T.Parallel(chunk_size, R, P):
|
|
346
|
+
# Apply SiLU to z_expanded_frag[cs, r, p]:
|
|
347
|
+
o_gated = z_frag[cs, p] * MIMO_Z[i_h, r, p] * 0.5
|
|
348
|
+
z_expanded_frag[cs, r, p] = o_gated * T.tanh(o_gated) + o_gated
|
|
349
|
+
|
|
350
|
+
lqk_PsiV_reshaped_frag = T.view(o_mimo_accum_frag, shape=[chunk_size, R, P])
|
|
351
|
+
if hasZ:
|
|
352
|
+
for cs, r, p in T.Parallel(chunk_size, R, P):
|
|
353
|
+
lqk_PsiV_reshaped_frag[cs, r, p] *= phi_frag_intrachunk[r, p] * z_expanded_frag[cs, r, p]
|
|
354
|
+
else:
|
|
355
|
+
for cs, r, p in T.Parallel(chunk_size, R, P):
|
|
356
|
+
lqk_PsiV_reshaped_frag[cs, r, p] *= phi_frag_intrachunk[r, p]
|
|
357
|
+
lqk_PsiV_reshaped_shared = T.alloc_shared([chunk_size, R, P], dtype)
|
|
358
|
+
T.copy(lqk_PsiV_reshaped_frag, lqk_PsiV_reshaped_shared)
|
|
359
|
+
o_frag = T.alloc_fragment([chunk_size, P], dtype)
|
|
360
|
+
T.clear(o_frag)
|
|
361
|
+
for r in T.serial(R):
|
|
362
|
+
for cs, p in T.Parallel(chunk_size, P):
|
|
363
|
+
o_frag[cs, p] += lqk_PsiV_reshaped_shared[cs, r, p]
|
|
364
|
+
T.copy(o_frag, O[i_b, chunk_start:chunk_start+chunk_size, i_h, :])
|
|
365
|
+
else:
|
|
366
|
+
if hasZ:
|
|
367
|
+
z_frag = T.alloc_fragment([chunk_size, P], dtype)
|
|
368
|
+
T.copy(Z[i_b, chunk_start:chunk_start+chunk_size, i_h, :], z_frag)
|
|
369
|
+
z_expanded_frag = T.alloc_fragment([chunk_size, R, P], dtype)
|
|
370
|
+
for cs, r, p in T.Parallel(chunk_size, R, P):
|
|
371
|
+
# Apply SiLU to z_expanded_frag[cs, r, p]:
|
|
372
|
+
o_gated = z_frag[cs, p] * MIMO_Z[i_h, r, p] * 0.5
|
|
373
|
+
z_expanded_frag[cs, r, p] = o_gated * T.tanh(o_gated) + o_gated
|
|
374
|
+
lqk_PsiV_reshaped_shared = T.alloc_shared([chunk_size, R, P], dtype)
|
|
375
|
+
for cs, r, p in T.Parallel(chunk_size, R, P):
|
|
376
|
+
lqk_PsiV_reshaped_shared[cs, r, p] = o_mimo_accum_frag[cs* R + r, p] * z_expanded_frag[cs, r, p]
|
|
377
|
+
# T.copy(lqk_PsiV_frag, lqk_PsiV_reshaped_shared)
|
|
378
|
+
# for cs, r, p in T.Parallel(chunk_size, R, P):
|
|
379
|
+
# lqk_PsiV_reshaped_shared[cs, r, p] *= z_expanded_frag[cs, r, p]
|
|
380
|
+
else:
|
|
381
|
+
lqk_PsiV_reshaped_shared = T.alloc_shared([chunk_size, R, P], dtype)
|
|
382
|
+
# T.copy(lqk_PsiV_reshaped_frag, lqk_PsiV_reshaped_shared)
|
|
383
|
+
for cs, r, p in T.Parallel(chunk_size, R, P):
|
|
384
|
+
lqk_PsiV_reshaped_shared[cs, r, p] = o_mimo_accum_frag[cs* R + r, p]
|
|
385
|
+
T.copy(lqk_PsiV_reshaped_shared, O[i_b, chunk_start:chunk_start+chunk_size, :, i_h, :])
|
|
386
|
+
|
|
387
|
+
# --- Recurrent State Update ---
|
|
388
|
+
# DA_CS_REV scales per-step K contributions for state accumulation.
|
|
389
|
+
dA_cs_rev_frag = T.alloc_fragment([chunk_size], T.float32)
|
|
390
|
+
T.copy(DA_CS_REV[i_b, i_h, chunk_start:chunk_start+chunk_size], dA_cs_rev_frag)
|
|
391
|
+
|
|
392
|
+
k_state_frag = T.alloc_fragment([fused_chunk_size, N], dtype)
|
|
393
|
+
T.copy(k_shared, k_state_frag)
|
|
394
|
+
for csr, n in T.Parallel(fused_chunk_size, N):
|
|
395
|
+
k_state_frag[csr, n] *= T.exp(dA_cs_rev_frag[csr//R])
|
|
396
|
+
|
|
397
|
+
# DA_CS(last) applies the chunk-level decay to the carried state.
|
|
398
|
+
da_cs_sum = T.alloc_var(T.float32)
|
|
399
|
+
if tail_len > 0 and i == nchunks - 1:
|
|
400
|
+
T.copy(DA_CS[i_b, i_h, S - 1], da_cs_sum)
|
|
401
|
+
if return_final_state:
|
|
402
|
+
for csr, n in T.Parallel(fused_chunk_size, N):
|
|
403
|
+
k_state_frag[csr, n] = T.if_then_else(csr < tail_len * R, k_state_frag[csr, n], 0.0)
|
|
404
|
+
else:
|
|
405
|
+
T.copy(DA_CS[i_b, i_h, chunk_start+chunk_size-1], da_cs_sum)
|
|
406
|
+
for n, p in T.Parallel(N, P):
|
|
407
|
+
states_frag[n, p] *= T.exp(da_cs_sum)
|
|
408
|
+
T.gemm(k_state_frag, PsiV_shared, states_frag, transpose_A=True, clear_accum=False)
|
|
409
|
+
|
|
410
|
+
# --- Save Last State (if applicable) ---
|
|
411
|
+
if return_final_state:
|
|
412
|
+
T.copy(states_frag, FINAL_STATE[i_b, i_h, :, :])
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
return mamba_mimo_fwd_kernel
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def mamba_mimo_forward(q, k, v,
|
|
419
|
+
q_bias, k_bias,
|
|
420
|
+
mimo_v, mimo_o,
|
|
421
|
+
z, D,
|
|
422
|
+
mimo_z,
|
|
423
|
+
angles,
|
|
424
|
+
dA_cs,
|
|
425
|
+
dA_cs_rev,
|
|
426
|
+
dt,
|
|
427
|
+
trap,
|
|
428
|
+
segsum,
|
|
429
|
+
chunk_size, rotary_dim_divisor, dtype,
|
|
430
|
+
return_state=False,
|
|
431
|
+
threads=128,
|
|
432
|
+
num_stages=0):
|
|
433
|
+
B, S, R, G, N = q.shape
|
|
434
|
+
H, P = v.shape[-2], v.shape[-1]
|
|
435
|
+
if isinstance(dtype, torch.dtype):
|
|
436
|
+
tl_dtype = str(dtype).replace("torch.", "")
|
|
437
|
+
else:
|
|
438
|
+
tl_dtype = dtype
|
|
439
|
+
reduceO = mimo_o is not None
|
|
440
|
+
kernel = mamba_mimo_fwd(_dynamic("B"),
|
|
441
|
+
_dynamic("S"),
|
|
442
|
+
_dynamic("H"),
|
|
443
|
+
_dynamic("G"),
|
|
444
|
+
N, P, R,
|
|
445
|
+
z is not None,
|
|
446
|
+
D is not None,
|
|
447
|
+
reduceO,
|
|
448
|
+
return_final_state=return_state,
|
|
449
|
+
chunk_size=chunk_size,
|
|
450
|
+
rotary_dim_divisor=rotary_dim_divisor,
|
|
451
|
+
dtype=tl_dtype,
|
|
452
|
+
threads=threads,
|
|
453
|
+
num_stages=num_stages)
|
|
454
|
+
# print(kernel.get_kernel_source()) # NOTE: prints compiled CUDA code
|
|
455
|
+
if reduceO:
|
|
456
|
+
o = torch.empty((B, S, H, P), device='cuda', dtype=dtype)
|
|
457
|
+
else:
|
|
458
|
+
o = torch.empty((B, S, R, H, P), device='cuda', dtype=dtype)
|
|
459
|
+
# Kernel always declares all tensor parameters; pass dummies for None args
|
|
460
|
+
mimo_o_arg = mimo_o if reduceO else torch.empty((H, R, P), device=q.device, dtype=torch.float32)
|
|
461
|
+
z_arg = z if z is not None else torch.empty((B, S, H, P), device=q.device, dtype=dtype)
|
|
462
|
+
D_arg = D if D is not None else torch.empty((H,), device=q.device, dtype=torch.float32)
|
|
463
|
+
mimo_z_arg = mimo_z if mimo_z is not None else torch.empty((H, R, P), device=q.device, dtype=torch.float32)
|
|
464
|
+
|
|
465
|
+
h = torch.empty((B, H, N, P), device='cuda', dtype=torch.float32) if return_state else None
|
|
466
|
+
k_final = torch.empty((B, R, H, N), device='cuda', dtype=dtype) if return_state else None
|
|
467
|
+
|
|
468
|
+
kernel( q,
|
|
469
|
+
k,
|
|
470
|
+
v, o,
|
|
471
|
+
q_bias, k_bias,
|
|
472
|
+
mimo_v, mimo_o_arg,
|
|
473
|
+
z_arg, D_arg, mimo_z_arg,
|
|
474
|
+
angles,
|
|
475
|
+
dA_cs,
|
|
476
|
+
dA_cs_rev,
|
|
477
|
+
dt,
|
|
478
|
+
trap,
|
|
479
|
+
segsum,
|
|
480
|
+
h,
|
|
481
|
+
k_final
|
|
482
|
+
)
|
|
483
|
+
return o, h, k_final
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""Copyright (c) 2026, Dao AI Lab, Goombalab. Apache-2.0; see LICENSE.
|
|
2
|
+
Modified by Continuous Intelligence: extracted forward-only inference utilities.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import torch
|
|
6
|
+
import triton
|
|
7
|
+
import triton.language as tl
|
|
8
|
+
|
|
9
|
+
@triton.autotune(
|
|
10
|
+
configs=[
|
|
11
|
+
triton.Config({}, num_stages=s, num_warps=w)
|
|
12
|
+
for s in [2, 3]
|
|
13
|
+
for w in [4, 8]
|
|
14
|
+
],
|
|
15
|
+
key=["CHUNK_SIZE"],
|
|
16
|
+
)
|
|
17
|
+
@triton.jit
|
|
18
|
+
def dacs_segsum_kernel(
|
|
19
|
+
da_ptr,
|
|
20
|
+
da_cs_ptr,
|
|
21
|
+
da_cs_rev_ptr,
|
|
22
|
+
segsum_ptr,
|
|
23
|
+
stride_da_batch, stride_da_head, stride_da_seq,
|
|
24
|
+
stride_da_cs_batch, stride_da_cs_head, stride_da_cs_seq,
|
|
25
|
+
stride_da_cs_rev_batch, stride_da_cs_rev_head, stride_da_cs_rev_seq,
|
|
26
|
+
stride_segsum_batch, stride_segsum_head, stride_segsum_chunk,
|
|
27
|
+
stride_segsum_row, stride_segsum_col,
|
|
28
|
+
SEQLEN: tl.constexpr,
|
|
29
|
+
CHUNK_SIZE: tl.constexpr,
|
|
30
|
+
):
|
|
31
|
+
"""Dense (fixed-length) version of dacs_segsum_kernel_varlen.
|
|
32
|
+
|
|
33
|
+
Computes da_cs, da_cs_rev, and the lower-triangular segsum matrix for each
|
|
34
|
+
contiguous chunk. Sequences are assumed to be fixed-length and aligned to
|
|
35
|
+
chunk boundaries; no varlen mapping tensors are needed.
|
|
36
|
+
|
|
37
|
+
Grid: (B, H, nchunks).
|
|
38
|
+
"""
|
|
39
|
+
pid_batch = tl.program_id(0)
|
|
40
|
+
pid_head = tl.program_id(1)
|
|
41
|
+
pid_chunk = tl.program_id(2)
|
|
42
|
+
|
|
43
|
+
chunk_start = pid_chunk * CHUNK_SIZE
|
|
44
|
+
offs = tl.arange(0, CHUNK_SIZE)
|
|
45
|
+
offs_seq = chunk_start + offs
|
|
46
|
+
mask = offs_seq < SEQLEN
|
|
47
|
+
|
|
48
|
+
base_da = pid_batch * stride_da_batch + pid_head * stride_da_head
|
|
49
|
+
da_chunk = tl.load(da_ptr + base_da + offs_seq * stride_da_seq, mask=mask, other=0.0)
|
|
50
|
+
|
|
51
|
+
da_cs = tl.cumsum(da_chunk, axis=0)
|
|
52
|
+
da_cs = tl.minimum(da_cs, 0.0)
|
|
53
|
+
|
|
54
|
+
da_cs_rev = tl.cumsum(da_chunk, axis=0, reverse=True)
|
|
55
|
+
# Roll one to the left:
|
|
56
|
+
i = tl.arange(0, CHUNK_SIZE)[:, None] # [N,1]
|
|
57
|
+
j = tl.arange(0, CHUNK_SIZE)[None, :] # [1,N]
|
|
58
|
+
S_mat = (i == j - 1) # strictly upper diagonal (one above main)
|
|
59
|
+
da_cs_rev = tl.sum(tl.where(S_mat, da_cs_rev, 0), axis=1)
|
|
60
|
+
da_cs_rev = tl.minimum(da_cs_rev, 0.0)
|
|
61
|
+
|
|
62
|
+
base_da_cs = pid_batch * stride_da_cs_batch + pid_head * stride_da_cs_head
|
|
63
|
+
base_da_cs_rev = pid_batch * stride_da_cs_rev_batch + pid_head * stride_da_cs_rev_head
|
|
64
|
+
tl.store(da_cs_ptr + base_da_cs + offs_seq * stride_da_cs_seq, da_cs, mask=mask)
|
|
65
|
+
tl.store(da_cs_rev_ptr + base_da_cs_rev + offs_seq * stride_da_cs_rev_seq, da_cs_rev, mask=mask)
|
|
66
|
+
|
|
67
|
+
broadcasted_indices = tl.zeros_like(offs)
|
|
68
|
+
segsum = tl.load(da_ptr + base_da + offs_seq[:, None] * stride_da_seq + broadcasted_indices[None, :])
|
|
69
|
+
offs_i = offs[:, None]
|
|
70
|
+
offs_j = offs[None, :]
|
|
71
|
+
segsum = tl.where(offs_i > offs_j, segsum, 0.0)
|
|
72
|
+
segsum = tl.cumsum(segsum, axis=0)
|
|
73
|
+
segsum = tl.minimum(segsum, 0.0)
|
|
74
|
+
|
|
75
|
+
base_segsum = (pid_batch * stride_segsum_batch +
|
|
76
|
+
pid_head * stride_segsum_head +
|
|
77
|
+
pid_chunk * stride_segsum_chunk)
|
|
78
|
+
tl.store(segsum_ptr + base_segsum + offs_i * stride_segsum_row + offs_j * stride_segsum_col, segsum)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def compute_dacs_segsum_triton(
|
|
82
|
+
da: torch.Tensor, # [B, H, S]
|
|
83
|
+
chunk_size: int,
|
|
84
|
+
):
|
|
85
|
+
"""Compute da_cs, da_cs_rev, and segsum for fixed-length sequences.
|
|
86
|
+
|
|
87
|
+
Returns
|
|
88
|
+
-------
|
|
89
|
+
da_cs : forward inclusive prefix-sum of da, clipped to ≤ 0. [B, H, S]
|
|
90
|
+
da_cs_rev: exclusive reverse prefix-sum of da, clipped to ≤ 0. [B, H, S]
|
|
91
|
+
segsum : lower-triangular intra-chunk segsum. [B, H, nchunks, C, C]
|
|
92
|
+
"""
|
|
93
|
+
B, H, S = da.shape
|
|
94
|
+
nchunks = (S + chunk_size - 1) // chunk_size
|
|
95
|
+
|
|
96
|
+
da_cs = torch.empty_like(da)
|
|
97
|
+
da_cs_rev = torch.empty_like(da)
|
|
98
|
+
segsum = torch.empty(B, H, nchunks, chunk_size, chunk_size, device=da.device, dtype=da.dtype)
|
|
99
|
+
|
|
100
|
+
grid = (B, H, nchunks)
|
|
101
|
+
dacs_segsum_kernel[grid](
|
|
102
|
+
da, da_cs, da_cs_rev, segsum,
|
|
103
|
+
da.stride(0), da.stride(1), da.stride(2),
|
|
104
|
+
da_cs.stride(0), da_cs.stride(1), da_cs.stride(2),
|
|
105
|
+
da_cs_rev.stride(0), da_cs_rev.stride(1), da_cs_rev.stride(2),
|
|
106
|
+
segsum.stride(0), segsum.stride(1), segsum.stride(2),
|
|
107
|
+
segsum.stride(3), segsum.stride(4),
|
|
108
|
+
S,
|
|
109
|
+
chunk_size,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
return da_cs, da_cs_rev, segsum
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Copyright (c) 2025, Dao AI Lab, Goombalab. Apache-2.0; see LICENSE.
|
|
2
|
+
Modified by Continuous Intelligence: extracted the inference utility needed by ci-proto.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import triton
|
|
6
|
+
import triton.language as tl
|
|
7
|
+
|
|
8
|
+
@triton.jit
|
|
9
|
+
def tanh_approx(x):
|
|
10
|
+
"""
|
|
11
|
+
(Fast) hyperbolic tangent approximation using PTX inline assembly.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
x: Input triton tensor (any shape) in float32
|
|
15
|
+
Returns:
|
|
16
|
+
Approximate tanh values in float32
|
|
17
|
+
"""
|
|
18
|
+
return tl.inline_asm_elementwise(
|
|
19
|
+
"tanh.approx.f32 $0, $1;",
|
|
20
|
+
constraints="=f,f",
|
|
21
|
+
args=[x],
|
|
22
|
+
dtype=tl.float32,
|
|
23
|
+
is_pure=True,
|
|
24
|
+
pack=1,
|
|
25
|
+
)
|