# SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project import inspect import torch # this import will also register the custom ops # import vllm.model_executor.kernels.mhc # noqa: F401 import vllm.model_executor.kernels.mhc as mhc_kernels from vllm._aiter_ops import is_aiter_found_and_supported from vllm.model_executor.custom_op import CustomOp from vllm.platforms import current_platform from vllm.utils.import_utils import has_tilelang def _has_tilelang_mhc() -> bool: if not has_tilelang(): return False if current_platform.is_cuda(): return True if current_platform.is_rocm(): from vllm.platforms.rocm import on_gfx942 # TileLang MHC currently produces incorrect results on gfx942. Keep # gfx942 on the existing torch/triton fallbacks until that path is fixed. return not on_gfx942() return False HAS_TILELANG_MHC = _has_tilelang_mhc() HAS_AITER_MHC = is_aiter_found_and_supported() def _has_aiter_mhc_fused() -> bool: if not HAS_AITER_MHC: return False try: from aiter.ops.mhc import mhc_fused_post_pre except Exception: return False return callable(mhc_fused_post_pre) def _aiter_mhc_op_accepts_norm(op_name: str) -> bool: if not HAS_AITER_MHC: return False try: from aiter.ops import mhc as aiter_mhc for candidate_name in (op_name, f"{op_name}_fake"): op = getattr(aiter_mhc, candidate_name, None) if op is None: continue parameters = inspect.signature(op).parameters if "norm_weight" in parameters and "norm_eps" in parameters: return True except (AttributeError, ImportError, TypeError, ValueError): pass return False HAS_AITER_MHC_FUSED = _has_aiter_mhc_fused() HAS_AITER_MHC_PRE_NORM = _aiter_mhc_op_accepts_norm("mhc_pre") HAS_AITER_MHC_FUSED_NORM = _aiter_mhc_op_accepts_norm("mhc_fused_post_pre") def _aiter_mhc_supported( residual: torch.Tensor, norm_weight: torch.Tensor | None, *, supports_norm: bool, ) -> bool: hidden_size = residual.shape[-1] hc_mult = residual.shape[-2] return ( HAS_AITER_MHC and hidden_size % 256 == 0 and hc_mult == 4 and (norm_weight is None or supports_norm) ) def _apply_mhc_norm( layer_input: torch.Tensor, norm_weight: torch.Tensor | None, norm_eps: float, ) -> torch.Tensor: if norm_weight is None: return layer_input from vllm import ir return ir.ops.rms_norm(layer_input, norm_weight, norm_eps) # --8<-- [start:mhc_pre] @CustomOp.register("mhc_pre") class MHCPreOp(CustomOp): """MHC pre block. Computes mix logits from RMS-normalized HC residual streams, then returns post_mix, comb_mix, and layer_input = sum_i pre_mix_i * residual_i. """ # --8<-- [end:mhc_pre] @classmethod def enabled(cls) -> bool: return True def forward_cuda( self, residual: torch.Tensor, fn: torch.Tensor, hc_scale: torch.Tensor, hc_base: torch.Tensor, rms_eps: float, hc_pre_eps: float, hc_sinkhorn_eps: float, hc_post_mult_value: float, sinkhorn_repeat: int, n_splits: int = 1, norm_weight: torch.Tensor | None = None, norm_eps: float = 0.0, ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: return torch.ops.vllm.mhc_pre_tilelang( residual, fn, hc_scale, hc_base, rms_eps, hc_pre_eps, hc_sinkhorn_eps, hc_post_mult_value, sinkhorn_repeat, n_splits, norm_weight, norm_eps, ) def forward_hip( self, residual: torch.Tensor, fn: torch.Tensor, hc_scale: torch.Tensor, hc_base: torch.Tensor, rms_eps: float, hc_pre_eps: float, hc_sinkhorn_eps: float, hc_post_mult_value: float, sinkhorn_repeat: int, n_splits: int = 1, norm_weight: torch.Tensor | None = None, norm_eps: float = 0.0, ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: if _aiter_mhc_supported( residual, norm_weight, supports_norm=HAS_AITER_MHC_PRE_NORM, ): return torch.ops.vllm.mhc_pre_aiter( residual, fn, hc_scale, hc_base, rms_eps, hc_pre_eps, hc_sinkhorn_eps, hc_post_mult_value, sinkhorn_repeat, n_splits, norm_weight, norm_eps, ) elif HAS_TILELANG_MHC: return torch.ops.vllm.mhc_pre_tilelang( residual, fn, hc_scale, hc_base, rms_eps, hc_pre_eps, hc_sinkhorn_eps, hc_post_mult_value, sinkhorn_repeat, n_splits, norm_weight, norm_eps, ) else: post_mix, comb_mix, layer_input = self.forward_native( residual, fn, hc_scale, hc_base, rms_eps, hc_pre_eps, hc_sinkhorn_eps, hc_post_mult_value, sinkhorn_repeat, n_splits, norm_weight, norm_eps, ) return ( post_mix, comb_mix, _apply_mhc_norm(layer_input, norm_weight, norm_eps), ) def forward_native( self, residual: torch.Tensor, fn: torch.Tensor, hc_scale: torch.Tensor, hc_base: torch.Tensor, rms_eps: float, hc_pre_eps: float, hc_sinkhorn_eps: float, hc_post_mult_value: float, sinkhorn_repeat: int, n_splits: int = 1, norm_weight: torch.Tensor | None = None, norm_eps: float = 0.0, ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: return mhc_kernels.mhc_pre_torch( residual, fn, hc_scale, hc_base, rms_eps, hc_pre_eps, hc_sinkhorn_eps, hc_post_mult_value, sinkhorn_repeat, ) def forward_xpu( self, residual: torch.Tensor, fn: torch.Tensor, hc_scale: torch.Tensor, hc_base: torch.Tensor, rms_eps: float, hc_pre_eps: float, hc_sinkhorn_eps: float, hc_post_mult_value: float, sinkhorn_repeat: int, n_splits: int = 1, norm_weight: torch.Tensor | None = None, norm_eps: float = 0.0, ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: return torch.ops._xpu_C.mhc_pre( residual, fn, hc_scale, hc_base, rms_eps, hc_pre_eps, hc_sinkhorn_eps, hc_post_mult_value, sinkhorn_repeat, ) def forward_cpu( self, residual: torch.Tensor, fn: torch.Tensor, hc_scale: torch.Tensor, hc_base: torch.Tensor, rms_eps: float, hc_pre_eps: float, hc_sinkhorn_eps: float, hc_post_mult_value: float, sinkhorn_repeat: int, n_splits: int = 1, norm_weight: torch.Tensor | None = None, norm_eps: float = 0.0, ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: return mhc_kernels.mhc_pre_cpu( residual, fn, hc_scale, hc_base, rms_eps, hc_pre_eps, hc_sinkhorn_eps, hc_post_mult_value, sinkhorn_repeat, n_splits, norm_weight, norm_eps, ) # --8<-- [start:mhc_pre_delayed] @CustomOp.register("mhc_pre_delayed") class MHCPreDelayedOp(CustomOp): """MHC pre block using the pre-mix carried from the previous sublayer. Same gates as :class:`MHCPreOp`, but the stream collapse applies the caller's ``pre_mix`` and this sublayer's pre-mix is returned for the next sublayer seam. Passing ``sublayer_out`` / ``post_layer_mix`` / ``comb_res_mix`` also applies the preceding post block, which lets AITER fold it into the pre projection and saves a launch. Returns residual, post_mix, comb_mix, layer_input, next_pre_mix; ``residual`` is the caller's own tensor when no post is requested. """ # --8<-- [end:mhc_pre_delayed] @classmethod def enabled(cls) -> bool: return True def __init__(self) -> None: super().__init__() # Built here, not lazily: CustomOp resolves dispatch against the # current vLLM config, which is only set during model construction. self._post = MHCPostOp() def _maybe_post( self, residual: torch.Tensor, sublayer_out: torch.Tensor | None, post_layer_mix: torch.Tensor | None, comb_res_mix: torch.Tensor | None, ) -> torch.Tensor: if sublayer_out is None: return residual assert post_layer_mix is not None and comb_res_mix is not None return self._post(sublayer_out, residual, post_layer_mix, comb_res_mix) def forward_cuda( self, residual: torch.Tensor, fn: torch.Tensor, hc_scale: torch.Tensor, hc_base: torch.Tensor, rms_eps: float, hc_pre_eps: float, hc_sinkhorn_eps: float, hc_post_mult_value: float, sinkhorn_repeat: int, pre_mix: torch.Tensor | None = None, x: torch.Tensor | None = None, norm_weight: torch.Tensor | None = None, norm_eps: float = 1e-6, sublayer_out: torch.Tensor | None = None, post_layer_mix: torch.Tensor | None = None, comb_res_mix: torch.Tensor | None = None, ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]: residual = self._maybe_post( residual, sublayer_out, post_layer_mix, comb_res_mix ) return residual, *torch.ops.vllm.mhc_pre_delayed_tilelang( residual, fn, hc_scale, hc_base, rms_eps, hc_pre_eps, hc_sinkhorn_eps, hc_post_mult_value, sinkhorn_repeat, pre_mix, x, norm_weight, norm_eps, ) def forward_hip( self, residual: torch.Tensor, fn: torch.Tensor, hc_scale: torch.Tensor, hc_base: torch.Tensor, rms_eps: float, hc_pre_eps: float, hc_sinkhorn_eps: float, hc_post_mult_value: float, sinkhorn_repeat: int, pre_mix: torch.Tensor | None = None, x: torch.Tensor | None = None, norm_weight: torch.Tensor | None = None, norm_eps: float = 1e-6, sublayer_out: torch.Tensor | None = None, post_layer_mix: torch.Tensor | None = None, comb_res_mix: torch.Tensor | None = None, ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]: # The AITER delayed path drives mhc_pre_gemm_sqrsum against `residual` # and folds no RMSNorm, so it cannot serve the model-entry broadcast # (which projects a narrower `x`) or a fused norm. Both are handled by # TileLang, or by the reference when TileLang is unavailable. if ( x is None and norm_weight is None and _aiter_mhc_supported(residual, None, supports_norm=False) ): from vllm._aiter_ops import rocm_aiter_ops num_tokens = residual.numel() // (residual.shape[-1] * residual.shape[-2]) # Folding the post in only pays while the residual still fits in # cache; past that AITER's heuristic wants the separate kernels, # which can ask for a non-temporal store and a large-m split-k. if sublayer_out is not None and ( rocm_aiter_ops.mhc_fused_post_pre_delayed_prefers_unfused(num_tokens) ): residual = self._maybe_post( residual, sublayer_out, post_layer_mix, comb_res_mix ) sublayer_out = None # The op writes the folded post's residual into this buffer rather # than returning it, since on the unfused path it has none of its # own and an op must not hand back one of its inputs. residual_out = ( torch.empty_like(residual) if sublayer_out is not None else None ) rest = torch.ops.vllm.mhc_pre_delayed_aiter( residual, fn, hc_scale, hc_base, rms_eps, hc_pre_eps, hc_sinkhorn_eps, hc_post_mult_value, sinkhorn_repeat, pre_mix, sublayer_out, post_layer_mix, comb_res_mix, residual_out, ) return (residual if residual_out is None else residual_out), *rest residual = self._maybe_post( residual, sublayer_out, post_layer_mix, comb_res_mix ) if HAS_TILELANG_MHC: return residual, *torch.ops.vllm.mhc_pre_delayed_tilelang( residual, fn, hc_scale, hc_base, rms_eps, hc_pre_eps, hc_sinkhorn_eps, hc_post_mult_value, sinkhorn_repeat, pre_mix, x, norm_weight, norm_eps, ) # The post is already applied, so it must not be requested again. return self.forward_native( residual, fn, hc_scale, hc_base, rms_eps, hc_pre_eps, hc_sinkhorn_eps, hc_post_mult_value, sinkhorn_repeat, pre_mix, x, norm_weight, norm_eps, ) def forward_native( self, residual: torch.Tensor, fn: torch.Tensor, hc_scale: torch.Tensor, hc_base: torch.Tensor, rms_eps: float, hc_pre_eps: float, hc_sinkhorn_eps: float, hc_post_mult_value: float, sinkhorn_repeat: int, pre_mix: torch.Tensor | None = None, x: torch.Tensor | None = None, norm_weight: torch.Tensor | None = None, norm_eps: float = 1e-6, sublayer_out: torch.Tensor | None = None, post_layer_mix: torch.Tensor | None = None, comb_res_mix: torch.Tensor | None = None, ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]: residual = self._maybe_post( residual, sublayer_out, post_layer_mix, comb_res_mix ) post_mix, comb_mix, layer_input, next_pre_mix = ( mhc_kernels.mhc_pre_delayed_torch( residual, fn, hc_scale, hc_base, rms_eps, hc_pre_eps, hc_sinkhorn_eps, hc_post_mult_value, sinkhorn_repeat, pre_mix=pre_mix, x=x, ) ) return ( residual, post_mix, comb_mix, _apply_mhc_norm(layer_input, norm_weight, norm_eps), next_pre_mix, ) # --8<-- [start:mhc_post] @CustomOp.register("mhc_post") class MHCPostOp(CustomOp): """MHC post block. Combines the layer output with the HC residual streams: out_j = post_layer_mix_j * x + sum_i comb_res_mix_ij * residual_i. """ # --8<-- [end:mhc_post] @classmethod def enabled(cls) -> bool: return True def forward_cuda( self, x: torch.Tensor, residual: torch.Tensor, post_layer_mix: torch.Tensor, comb_res_mix: torch.Tensor, ) -> torch.Tensor: return torch.ops.vllm.mhc_post_tilelang( x, residual, post_layer_mix, comb_res_mix ) def forward_hip( self, x: torch.Tensor, residual: torch.Tensor, post_layer_mix: torch.Tensor, comb_res_mix: torch.Tensor, ) -> torch.Tensor: if _aiter_mhc_supported(residual, None, supports_norm=True): return torch.ops.vllm.mhc_post_aiter( x, residual, post_layer_mix, comb_res_mix, ) if HAS_TILELANG_MHC: return torch.ops.vllm.mhc_post_tilelang( x, residual, post_layer_mix, comb_res_mix ) else: return self.forward_native(x, residual, post_layer_mix, comb_res_mix) def forward_native( self, x: torch.Tensor, residual: torch.Tensor, post_layer_mix: torch.Tensor, comb_res_mix: torch.Tensor, ) -> torch.Tensor: return mhc_kernels.mhc_post_torch( x, residual, post_layer_mix, comb_res_mix, ) def forward_xpu( self, x: torch.Tensor, residual: torch.Tensor, post_layer_mix: torch.Tensor, comb_res_mix: torch.Tensor, ) -> torch.Tensor: return torch.ops._xpu_C.mhc_post( x, residual, post_layer_mix, comb_res_mix, ) def forward_cpu( self, x: torch.Tensor, residual: torch.Tensor, post_layer_mix: torch.Tensor, comb_res_mix: torch.Tensor, ) -> torch.Tensor: return mhc_kernels.mhc_post_cpu(x, residual, post_layer_mix, comb_res_mix) # --8<-- [start:hc_head] @CustomOp.register("hc_head") class HCHeadOp(CustomOp): """HC head reduction for DeepSeek V4. Computes gates from the RMS-normalized flattened HC residual and returns out = sum_i gate_i * residual_i, collapsing hc_mult streams to one. """ # --8<-- [end:hc_head] @classmethod def enabled(cls) -> bool: return True def forward_cuda( self, hidden_states: torch.Tensor, hc_fn: torch.Tensor, hc_scale: torch.Tensor, hc_base: torch.Tensor, rms_norm_eps: float, hc_eps: float, ) -> torch.Tensor: hc_mult, hidden_size = hidden_states.shape[-2:] outer_shape = hidden_states.shape[:-2] hs_flat = hidden_states.view(-1, hc_mult, hidden_size) out = torch.ops.vllm.hc_head_fused_kernel_tilelang( hs_flat, hc_fn, hc_scale, hc_base, rms_norm_eps, hc_eps, ) return out.view(*outer_shape, hidden_size) def forward_hip( self, hidden_states: torch.Tensor, hc_fn: torch.Tensor, hc_scale: torch.Tensor, hc_base: torch.Tensor, rms_norm_eps: float, hc_eps: float, ) -> torch.Tensor: hc_mult, hidden_size = hidden_states.shape[-2:] outer_shape = hidden_states.shape[:-2] hs_flat = hidden_states.view(-1, hc_mult, hidden_size) if HAS_TILELANG_MHC: out = torch.ops.vllm.hc_head_fused_kernel_tilelang( hs_flat, hc_fn, hc_scale, hc_base, rms_norm_eps, hc_eps, ) else: num_tokens = hs_flat.shape[0] out = torch.empty( num_tokens, hidden_size, dtype=torch.bfloat16, device=hidden_states.device, ) torch.ops.vllm.hc_head_triton( hs_flat, hc_fn, hc_scale, hc_base, out, hidden_size, rms_norm_eps, hc_eps, hc_mult, ) return out.view(*outer_shape, hidden_size) def forward_native(self, *args, **kwargs): raise NotImplementedError("Native implementation of hc_head is not available") def forward_xpu( self, hidden_states: torch.Tensor, hc_fn: torch.Tensor, hc_scale: torch.Tensor, hc_base: torch.Tensor, rms_norm_eps: float, hc_eps: float, ) -> torch.Tensor: hc_mult, hidden_size = hidden_states.shape[-2:] outer_shape = hidden_states.shape[:-2] hs_flat = hidden_states.view(-1, hc_mult, hidden_size) num_tokens = hs_flat.shape[0] out = torch.empty( num_tokens, hidden_size, dtype=torch.bfloat16, device=hidden_states.device ) torch.ops._xpu_C.hc_head_fused( hs_flat, hc_fn, hc_scale, hc_base, out, rms_norm_eps, hc_eps ) return out.view(*outer_shape, hidden_size) def forward_cpu( self, hidden_states: torch.Tensor, hc_fn: torch.Tensor, hc_scale: torch.Tensor, hc_base: torch.Tensor, rms_norm_eps: float, hc_eps: float, ) -> torch.Tensor: return mhc_kernels.hc_head_fused_cpu( hidden_states, hc_fn, hc_scale, hc_base, rms_norm_eps, hc_eps ) # --8<-- [start:mhc_fused_post_pre] @CustomOp.register("mhc_fused_post_pre") class MHCFusedPostPreOp(CustomOp): """Fused MHC post block followed by the next MHC pre block. Equivalent to applying MHCPostOp and then MHCPreOp to the updated residual streams, returning residual_cur, post_mix_cur, comb_mix_cur, and layer_input_cur. """ # --8<-- [end:mhc_fused_post_pre] @classmethod def enabled(cls) -> bool: return True def forward_cuda( self, x: torch.Tensor, residual: torch.Tensor, post_layer_mix: torch.Tensor, comb_res_mix: torch.Tensor, fn: torch.Tensor, hc_scale: torch.Tensor, hc_base: torch.Tensor, rms_eps: float, hc_pre_eps: float, hc_sinkhorn_eps: float, hc_post_mult_value: float, sinkhorn_repeat: int, n_splits: int = 1, tile_n: int = 1, norm_weight: torch.Tensor | None = None, norm_eps: float = 0.0, ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]: return torch.ops.vllm.mhc_fused_post_pre_tilelang( x, residual, post_layer_mix, comb_res_mix, fn, hc_scale, hc_base, rms_eps, hc_pre_eps, hc_sinkhorn_eps, hc_post_mult_value, sinkhorn_repeat, n_splits, tile_n, norm_weight, norm_eps, ) def forward_hip( self, x: torch.Tensor, residual: torch.Tensor, post_layer_mix: torch.Tensor, comb_res_mix: torch.Tensor, fn: torch.Tensor, hc_scale: torch.Tensor, hc_base: torch.Tensor, rms_eps: float, hc_pre_eps: float, hc_sinkhorn_eps: float, hc_post_mult_value: float, sinkhorn_repeat: int, n_splits: int = 1, tile_n: int = 1, norm_weight: torch.Tensor | None = None, norm_eps: float = 0.0, ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]: if HAS_AITER_MHC_FUSED and _aiter_mhc_supported( residual, norm_weight, supports_norm=HAS_AITER_MHC_FUSED_NORM, ): return torch.ops.vllm.mhc_fused_post_pre_aiter( x, residual, post_layer_mix, comb_res_mix, fn, hc_scale, hc_base, rms_eps, hc_pre_eps, hc_sinkhorn_eps, hc_post_mult_value, sinkhorn_repeat, n_splits, tile_n, norm_weight, norm_eps, ) if HAS_TILELANG_MHC: return torch.ops.vllm.mhc_fused_post_pre_tilelang( x, residual, post_layer_mix, comb_res_mix, fn, hc_scale, hc_base, rms_eps, hc_pre_eps, hc_sinkhorn_eps, hc_post_mult_value, sinkhorn_repeat, n_splits, tile_n, norm_weight, norm_eps, ) residual_cur, post_mix_cur, comb_mix_cur, layer_input_cur = self.forward_native( x, residual, post_layer_mix, comb_res_mix, fn, hc_scale, hc_base, rms_eps, hc_pre_eps, hc_sinkhorn_eps, hc_post_mult_value, sinkhorn_repeat, n_splits, tile_n, norm_weight, norm_eps, ) return ( residual_cur, post_mix_cur, comb_mix_cur, _apply_mhc_norm(layer_input_cur, norm_weight, norm_eps), ) def forward_native( self, x: torch.Tensor, residual: torch.Tensor, post_layer_mix: torch.Tensor, comb_res_mix: torch.Tensor, fn: torch.Tensor, hc_scale: torch.Tensor, hc_base: torch.Tensor, rms_eps: float, hc_pre_eps: float, hc_sinkhorn_eps: float, hc_post_mult_value: float, sinkhorn_repeat: int, n_splits: int = 1, tile_n: int = 1, norm_weight: torch.Tensor | None = None, norm_eps: float = 0.0, ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]: # Decompose into post + pre (no fused kernel available). residual_cur = mhc_kernels.mhc_post_torch( x, residual, post_layer_mix, comb_res_mix ) post_mix_cur, comb_mix_cur, layer_input_cur = mhc_kernels.mhc_pre_torch( residual_cur, fn, hc_scale, hc_base, rms_eps, hc_pre_eps, hc_sinkhorn_eps, hc_post_mult_value, sinkhorn_repeat, ) return residual_cur, post_mix_cur, comb_mix_cur, layer_input_cur def forward_xpu( self, x: torch.Tensor, residual: torch.Tensor, post_layer_mix: torch.Tensor, comb_res_mix: torch.Tensor, fn: torch.Tensor, hc_scale: torch.Tensor, hc_base: torch.Tensor, rms_eps: float, hc_pre_eps: float, hc_sinkhorn_eps: float, hc_post_mult_value: float, sinkhorn_repeat: int, n_splits: int = 1, tile_n: int = 1, norm_weight: torch.Tensor | None = None, norm_eps: float = 0.0, ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]: return torch.ops._xpu_C.mhc_fused_post_pre( x, residual, post_layer_mix, comb_res_mix, fn, hc_scale, hc_base, rms_eps, hc_pre_eps, hc_sinkhorn_eps, hc_post_mult_value, sinkhorn_repeat, ) def forward_cpu( self, x: torch.Tensor, residual: torch.Tensor, post_layer_mix: torch.Tensor, comb_res_mix: torch.Tensor, fn: torch.Tensor, hc_scale: torch.Tensor, hc_base: torch.Tensor, rms_eps: float, hc_pre_eps: float, hc_sinkhorn_eps: float, hc_post_mult_value: float, sinkhorn_repeat: int, n_splits: int = 1, tile_n: int = 1, norm_weight: torch.Tensor | None = None, norm_eps: float = 0.0, ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]: # Decompose into post + pre (no fused kernel available). residual_cur = mhc_kernels.mhc_post_cpu( x, residual, post_layer_mix, comb_res_mix ) post_mix_cur, comb_mix_cur, layer_input_cur = mhc_kernels.mhc_pre_cpu( residual_cur, fn, hc_scale, hc_base, rms_eps, hc_pre_eps, hc_sinkhorn_eps, hc_post_mult_value, sinkhorn_repeat, ) return residual_cur, post_mix_cur, comb_mix_cur, layer_input_cur def hc_expand(x: torch.Tensor, n: int) -> torch.Tensor: """[s, hidden_size] -> [s, n * hidden_size] by replication.""" return x.unsqueeze(1).expand(-1, n, -1).contiguous() def hc_contract(x: torch.Tensor, n: int) -> torch.Tensor: """[s, n * hidden_size] -> [s, hidden_size] by averaging.""" return x.mean(dim=1)