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.
Files changed (30) hide show
  1. continuousintelligence/__init__.py +9 -0
  2. continuousintelligence/_data/NOTICE +16 -0
  3. continuousintelligence/_data/bike_sharing.csv +145 -0
  4. continuousintelligence/_example.py +20 -0
  5. continuousintelligence/_loader.py +140 -0
  6. continuousintelligence/_plot.py +158 -0
  7. continuousintelligence/_vendor/__init__.py +1 -0
  8. continuousintelligence/_vendor/mamba3/LICENSE +201 -0
  9. continuousintelligence/_vendor/mamba3/__init__.py +6 -0
  10. continuousintelligence/_vendor/mamba3/angle_dt.py +213 -0
  11. continuousintelligence/_vendor/mamba3/layernorm_gated.py +115 -0
  12. continuousintelligence/_vendor/mamba3/mamba3_mimo_fwd.py +483 -0
  13. continuousintelligence/_vendor/mamba3/mamba3_mimo_utils.py +112 -0
  14. continuousintelligence/_vendor/mamba3/utils.py +25 -0
  15. continuousintelligence/ci_proto/__init__.py +5 -0
  16. continuousintelligence/ci_proto/_calendar.py +153 -0
  17. continuousintelligence/ci_proto/_checkpoint.py +106 -0
  18. continuousintelligence/ci_proto/_cuda.py +135 -0
  19. continuousintelligence/ci_proto/_forecast.py +708 -0
  20. continuousintelligence/ci_proto/_groups.py +198 -0
  21. continuousintelligence/ci_proto/_mamba.py +190 -0
  22. continuousintelligence/ci_proto/_model.py +489 -0
  23. continuousintelligence-0.1.0.dist-info/METADATA +29 -0
  24. continuousintelligence-0.1.0.dist-info/RECORD +30 -0
  25. continuousintelligence-0.1.0.dist-info/WHEEL +5 -0
  26. continuousintelligence-0.1.0.dist-info/licenses/LICENCE +95 -0
  27. continuousintelligence-0.1.0.dist-info/licenses/NOTICE +33 -0
  28. continuousintelligence-0.1.0.dist-info/licenses/src/continuousintelligence/_data/NOTICE +16 -0
  29. continuousintelligence-0.1.0.dist-info/licenses/src/continuousintelligence/_vendor/mamba3/LICENSE +201 -0
  30. 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
+ )
@@ -0,0 +1,5 @@
1
+ """ci-proto model implementation and quantile forecasting."""
2
+
3
+ from ._forecast import Forecaster
4
+
5
+ __all__ = ["Forecaster"]