# Copyright (c) 2026 LightSeek Foundation # # Portions copyright the vLLM project contributors under Apache-2.0. from __future__ import annotations from functools import cache import torch import triton import triton.language as tl from tokenspeed.runtime.utils import ceil_div try: from tokenspeed_kernel.thirdparty import deep_gemm except Exception: deep_gemm = None # type: ignore[assignment] @cache def _compute_num_split(block_k: int, k: int | None, grid_size: int) -> int: device_props = torch.cuda.get_device_properties(0) split_k = device_props.multi_processor_count // grid_size if k is not None: num_block_k = ceil_div(k, block_k) split_k = min(split_k, num_block_k // 4) return max(split_k, 1) @triton.jit def _load_reduced_mix( gemm_out_mul, token_id, mix_id: tl.constexpr, num_tokens, hc_mult3: tl.constexpr, n_splits: tl.constexpr, ): value = tl.full((), 0.0, tl.float32) for split_id in tl.static_range(0, n_splits): offset = split_id * num_tokens * hc_mult3 + token_id * hc_mult3 + mix_id value += tl.load(gemm_out_mul + offset) return value @triton.jit def _mhc_pre_mix_triton_kernel( gemm_out_mul, gemm_out_sqrsum, hc_scale, hc_base, pre_mix, post_mix, comb_mix, hidden_size: tl.constexpr, rms_eps: tl.constexpr, hc_eps: tl.constexpr, sinkhorn_iters: tl.constexpr, n_splits: tl.constexpr, hc_mult: tl.constexpr, hc_mult2: tl.constexpr, hc_mult3: tl.constexpr, block_comb: tl.constexpr, num_tokens, ): token_id = tl.program_id(0) rms_sum = tl.full((), 0.0, tl.float32) for split_id in tl.static_range(0, n_splits): rms_sum += tl.load(gemm_out_sqrsum + split_id * num_tokens + token_id) rms = tl.rsqrt(rms_sum / (hc_mult * hidden_size) + rms_eps) pre_scale = tl.load(hc_scale) for hc_id in tl.static_range(0, hc_mult): mix = _load_reduced_mix( gemm_out_mul, token_id, hc_id, num_tokens, hc_mult3, n_splits, ) pre = tl.sigmoid(mix * rms * pre_scale + tl.load(hc_base + hc_id)) + hc_eps tl.store(pre_mix + token_id * hc_mult + hc_id, pre) post_scale = tl.load(hc_scale + 1) for hc_id in tl.static_range(0, hc_mult): mix = _load_reduced_mix( gemm_out_mul, token_id, hc_mult + hc_id, num_tokens, hc_mult3, n_splits, ) post = ( tl.sigmoid(mix * rms * post_scale + tl.load(hc_base + hc_mult + hc_id)) * 2.0 ) tl.store(post_mix + token_id * hc_mult + hc_id, post) comb_offsets = tl.arange(0, block_comb) comb_mask = comb_offsets < hc_mult2 comb_scale = tl.load(hc_scale + 2) comb_mix_values = tl.zeros((block_comb,), tl.float32) for split_id in tl.static_range(0, n_splits): split_base = split_id * num_tokens * hc_mult3 + token_id * hc_mult3 comb_mix_values += tl.load( gemm_out_mul + split_base + hc_mult * 2 + comb_offsets, mask=comb_mask, other=0.0, ) comb_values = comb_mix_values * rms * comb_scale + tl.load( hc_base + hc_mult * 2 + comb_offsets, mask=comb_mask, other=0.0 ) rows = comb_offsets // hc_mult cols = comb_offsets - rows * hc_mult active = comb_mask for row_id in tl.static_range(0, hc_mult): row_values = tl.where((rows == row_id) & active, comb_values, -float("inf")) row_max = tl.max(row_values, axis=0) comb_values = tl.where( (rows == row_id) & active, tl.exp(comb_values - row_max), comb_values ) for row_id in tl.static_range(0, hc_mult): row_sum = tl.sum(tl.where((rows == row_id) & active, comb_values, 0.0), axis=0) comb_values = tl.where( (rows == row_id) & active, comb_values / row_sum + hc_eps, comb_values ) for col_id in tl.static_range(0, hc_mult): col_sum = tl.sum(tl.where((cols == col_id) & active, comb_values, 0.0), axis=0) comb_values = tl.where( (cols == col_id) & active, comb_values / (col_sum + hc_eps), comb_values, ) for _ in tl.static_range(1, sinkhorn_iters): for row_id in tl.static_range(0, hc_mult): row_sum = tl.sum( tl.where((rows == row_id) & active, comb_values, 0.0), axis=0 ) comb_values = tl.where( (rows == row_id) & active, comb_values / (row_sum + hc_eps), comb_values, ) for col_id in tl.static_range(0, hc_mult): col_sum = tl.sum( tl.where((cols == col_id) & active, comb_values, 0.0), axis=0 ) comb_values = tl.where( (cols == col_id) & active, comb_values / (col_sum + hc_eps), comb_values, ) tl.store( comb_mix + token_id * hc_mult2 + comb_offsets, comb_values, mask=comb_mask, ) @triton.jit def _mhc_pre_layer_triton_kernel( pre_mix, residual, layer_input, hidden_size: tl.constexpr, hc_mult: tl.constexpr, block_h: tl.constexpr, ): token_id = tl.program_id(0) hidden_block_id = tl.program_id(1) hidden_offsets = hidden_block_id * block_h + tl.arange(0, block_h) hidden_mask = hidden_offsets < hidden_size layer_acc = tl.zeros((block_h,), tl.float32) for hc_id in tl.static_range(0, hc_mult): pre = tl.load(pre_mix + token_id * hc_mult + hc_id).to(tl.float32) residual_offsets = ( token_id * hc_mult * hidden_size + hc_id * hidden_size + hidden_offsets ) residual_values = tl.load( residual + residual_offsets, mask=hidden_mask, other=0.0 ).to(tl.float32) layer_acc += pre * residual_values tl.store( layer_input + token_id * hidden_size + hidden_offsets, layer_acc, mask=hidden_mask, ) @triton.jit def _mhc_post_triton_kernel( comb, residual, post, hidden_states, out, hidden_size: tl.constexpr, hc_mult: tl.constexpr, block_h: tl.constexpr, ): token_id = tl.program_id(0) hidden_block_id = tl.program_id(1) hidden_offsets = hidden_block_id * block_h + tl.arange(0, block_h) hidden_mask = hidden_offsets < hidden_size hidden_values = tl.load( hidden_states + token_id * hidden_size + hidden_offsets, mask=hidden_mask, other=0.0, ).to(tl.float32) for out_hc in tl.static_range(0, hc_mult): acc = tl.load(post + token_id * hc_mult + out_hc).to(tl.float32) * hidden_values for in_hc in tl.static_range(0, hc_mult): comb_value = tl.load( comb + token_id * hc_mult * hc_mult + in_hc * hc_mult + out_hc ).to(tl.float32) residual_values = tl.load( residual + token_id * hc_mult * hidden_size + in_hc * hidden_size + hidden_offsets, mask=hidden_mask, other=0.0, ).to(tl.float32) acc += comb_value * residual_values tl.store( out + token_id * hc_mult * hidden_size + out_hc * hidden_size + hidden_offsets, acc, mask=hidden_mask, ) @triton.jit def _mhc_post_hc4_triton_kernel( comb, residual, post, hidden_states, out, hidden_size: tl.constexpr, block_h: tl.constexpr, ): token_id = tl.program_id(0) hidden_block_id = tl.program_id(1) hidden_offsets = hidden_block_id * block_h + tl.arange(0, block_h) hidden_mask = hidden_offsets < hidden_size token_hidden_offset = token_id * hidden_size token_residual_offset = token_id * 4 * hidden_size hidden_values = tl.load( hidden_states + token_hidden_offset + hidden_offsets, mask=hidden_mask, other=0.0, ).to(tl.float32) post_base = token_id * 4 acc0 = tl.load(post + post_base + 0).to(tl.float32) * hidden_values acc1 = tl.load(post + post_base + 1).to(tl.float32) * hidden_values acc2 = tl.load(post + post_base + 2).to(tl.float32) * hidden_values acc3 = tl.load(post + post_base + 3).to(tl.float32) * hidden_values comb_base = token_id * 16 for in_hc in tl.static_range(0, 4): residual_values = tl.load( residual + token_residual_offset + in_hc * hidden_size + hidden_offsets, mask=hidden_mask, other=0.0, ).to(tl.float32) comb_row = comb_base + in_hc * 4 acc0 += tl.load(comb + comb_row + 0).to(tl.float32) * residual_values acc1 += tl.load(comb + comb_row + 1).to(tl.float32) * residual_values acc2 += tl.load(comb + comb_row + 2).to(tl.float32) * residual_values acc3 += tl.load(comb + comb_row + 3).to(tl.float32) * residual_values tl.store( out + token_residual_offset + hidden_offsets, acc0, mask=hidden_mask, ) tl.store( out + token_residual_offset + hidden_size + hidden_offsets, acc1, mask=hidden_mask, ) tl.store( out + token_residual_offset + hidden_size * 2 + hidden_offsets, acc2, mask=hidden_mask, ) tl.store( out + token_residual_offset + hidden_size * 3 + hidden_offsets, acc3, mask=hidden_mask, ) def mhc_fused_hc( x_prev: torch.Tensor, residual_prev: torch.Tensor, post_prev: torch.Tensor, comb_prev: torch.Tensor, fn: torch.Tensor, hc_scale: torch.Tensor, hc_base: torch.Tensor, rms_eps: float, hc_eps: float, sinkhorn_iters: int, ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]: """Fused post_mapping(prev) + pre_mapping(curr). Returns (residual_cur, layer_input, post_cur, comb_cur). """ residual_cur = mhc_post(x_prev, residual_prev, post_prev, comb_prev) layer_input, post_cur, comb_cur = mhc_pre( residual_cur, fn, hc_scale, hc_base, rms_eps, hc_eps, sinkhorn_iters, ) return residual_cur, layer_input, post_cur, comb_cur def mhc_pre( residual: torch.Tensor, fn: torch.Tensor, hc_scale: torch.Tensor, hc_base: torch.Tensor, rms_eps: float, hc_eps: float, sinkhorn_iters: int, ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: if residual.dtype != torch.bfloat16 or fn.dtype != torch.float32: raise RuntimeError("fast mHC requires bf16 residual and fp32 weights") if not residual.is_cuda: raise RuntimeError("fast mHC requires CUDA tensors") if deep_gemm is None: raise RuntimeError("deep_gemm.tf32_hc_prenorm_gemm is unavailable") hc_mult = residual.shape[-2] hidden_size = residual.shape[-1] hc_mult2 = hc_mult * hc_mult hc_mult3 = hc_mult * 2 + hc_mult2 hc_hidden_size = hc_mult * hidden_size outer_shape = residual.shape[:-2] residual_flat = residual.view(-1, hc_mult, hidden_size) num_tokens = residual_flat.shape[0] if num_tokens == 0: return ( residual.new_empty(*outer_shape, hidden_size), torch.empty( *outer_shape, hc_mult, 1, dtype=torch.float32, device=residual.device, ), torch.empty( *outer_shape, hc_mult, hc_mult, dtype=torch.float32, device=residual.device, ), ) block_k = 64 block_m = 64 n_splits = _compute_num_split( block_k, hc_hidden_size, ceil_div(num_tokens, block_m) ) post_mix = torch.empty( num_tokens, hc_mult, dtype=torch.float32, device=residual.device ) pre_mix = torch.empty( num_tokens, hc_mult, dtype=torch.float32, device=residual.device ) comb_mix = torch.empty( num_tokens, hc_mult2, dtype=torch.float32, device=residual.device ) layer_input = torch.empty( num_tokens, hidden_size, dtype=torch.bfloat16, device=residual.device ) gemm_out_mul = torch.empty( n_splits, num_tokens, hc_mult3, dtype=torch.float32, device=residual.device ) gemm_out_sqrsum = torch.empty( n_splits, num_tokens, dtype=torch.float32, device=residual.device ) deep_gemm.tf32_hc_prenorm_gemm( residual_flat.view(num_tokens, hc_hidden_size), fn, gemm_out_mul, gemm_out_sqrsum, n_splits, ) block_h = 1024 block_comb = triton.next_power_of_2(hc_mult2) _mhc_pre_mix_triton_kernel[(num_tokens,)]( gemm_out_mul, gemm_out_sqrsum, hc_scale, hc_base, pre_mix, post_mix, comb_mix, hidden_size=hidden_size, rms_eps=rms_eps, hc_eps=hc_eps, sinkhorn_iters=sinkhorn_iters, n_splits=n_splits, hc_mult=hc_mult, hc_mult2=hc_mult2, hc_mult3=hc_mult3, block_comb=block_comb, num_tokens=num_tokens, num_warps=1, ) _mhc_pre_layer_triton_kernel[(num_tokens, triton.cdiv(hidden_size, block_h))]( pre_mix, residual_flat, layer_input, hidden_size=hidden_size, hc_mult=hc_mult, block_h=block_h, num_warps=4, ) return ( layer_input.view(*outer_shape, hidden_size), post_mix.view(*outer_shape, hc_mult, 1), comb_mix.view(*outer_shape, hc_mult, hc_mult), ) def mhc_post( hidden_states: torch.Tensor, residual: torch.Tensor, post: torch.Tensor, comb: torch.Tensor, ) -> torch.Tensor: if not hidden_states.is_cuda: raise RuntimeError("fast mHC requires CUDA tensors") if residual.numel() == 0: return torch.empty_like(residual) out = torch.empty_like(residual) hc_mult = residual.shape[-2] hidden_size = residual.shape[-1] residual_flat = residual.view(-1, hc_mult, hidden_size) hidden_states_flat = hidden_states.view(-1, hidden_size) post_flat = post.view(-1, hc_mult) comb_flat = comb.view(-1, hc_mult, hc_mult) num_tokens = residual_flat.shape[0] if hc_mult == 4: block_h = 256 _mhc_post_hc4_triton_kernel[(num_tokens, triton.cdiv(hidden_size, block_h))]( comb_flat, residual_flat, post_flat, hidden_states_flat, out, hidden_size=hidden_size, block_h=block_h, num_warps=4, ) return out block_h = 1024 _mhc_post_triton_kernel[(num_tokens, triton.cdiv(hidden_size, block_h))]( comb_flat, residual_flat, post_flat, hidden_states_flat, out, hidden_size=hidden_size, hc_mult=hc_mult, block_h=block_h, num_warps=4, ) return out