1# Copyright 2026 Huawei Technologies Co., Ltd
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ============================================================================
15"""KDA context-parallel executors: state CP, Ulysses and their combinations."""
16# pylint: disable=forbidden-backend-import
17from __future__ import annotations
18
19__all__ = [
20 "KimiDeltaAttentionCoreCP",
21 "KimiDeltaAttentionLayerCP",
22 "KimiDeltaAttentionLayerP2PCP",
23 "KimiDeltaAttentionLayerUlyssesCP",
24 "KimiDeltaAttentionP2PCP",
25 "KimiDeltaAttentionUlyssesCP",
26]
27
28from typing import Any, Optional
29
30import torch
31import torch.distributed as dist
32from torch import nn
33from torch.nn import functional as F
34
35from hyper_parallel.core.context_parallel.context_parallel import _ensure_1d
36from hyper_parallel.core.dtensor.device_mesh import DeviceMesh
37from hyper_parallel.core.dtensor.dtensor import DTensor
38from hyper_parallel.components.modules.kimi_delta_attention import (
39 chunk_kda,
40 torch_apply_kda_state_summary,
41 torch_kda_state_summary,
42)
43from hyper_parallel.core.utils import communication
44from hyper_parallel.distributed.context_parallel.kimi_delta_attention_state import (
45 build_kda_state_cp,
46 split_kda_mesh,
47)
48
49
50_KDA_BACKENDS = frozenset({"eager", "triton"})
51
52
53def _run_local_kda(
54 query: torch.Tensor,
55 key: torch.Tensor,
56 value: torch.Tensor,
57 gate: torch.Tensor,
58 beta: torch.Tensor,
59 *,
60 a_log: torch.Tensor,
61 dt_bias: torch.Tensor,
62 scale: Optional[float],
63 lower_bound: float,
64 chunk_size: int,
65 safe_gate: bool,
66 backend: str,
67) -> torch.Tensor:
68 """Run the explicitly selected local KDA implementation."""
69 if backend == "triton" and not safe_gate:
70 raise RuntimeError("The fused KDA backend requires the lower-bounded gate.")
71 output, _ = chunk_kda(
72 query,
73 key,
74 value,
75 gate,
76 beta,
77 a_log=a_log,
78 dt_bias=dt_bias,
79 scale=scale,
80 lower_bound=lower_bound,
81 chunk_size=chunk_size,
82 initial_state=None,
83 output_final_state=False,
84 use_qk_l2norm_in_kernel=True,
85 use_gate_in_kernel=True,
86 use_beta_sigmoid_in_kernel=True,
87 backend=backend,
88 )
89 return output
90
91
92def _is_official_kimi_layer(module: nn.Module) -> bool:
93 """Whether ``module`` follows the official Kimi K3 projection contract."""
94 return hasattr(module, "f_a_proj") and hasattr(module, "f_b_proj")
95
96
97def _num_value_heads(module: nn.Module) -> int:
98 """Return the number of KDA value heads for either supported layer API."""
99 if hasattr(module, "num_v_heads"):
100 return int(module.num_v_heads)
101 return int(module.num_heads)
102
103
104def _value_head_dim(module: nn.Module) -> int:
105 """Return the KDA value head width for either supported layer API."""
106 if hasattr(module, "head_v_dim"):
107 return int(module.head_v_dim)
108 return int(module.head_dim)
109
110
111def _key_projection_dim(module: nn.Module) -> int:
112 """Return the flattened Q/K projection width."""
113 if hasattr(module, "key_dim"):
114 return int(module.key_dim)
115 return int(module.num_heads * module.head_k_dim)
116
117
118def _value_projection_dim(module: nn.Module) -> int:
119 """Return the flattened value projection width."""
120 if hasattr(module, "value_dim"):
121 return int(module.value_dim)
122 return int(_num_value_heads(module) * _value_head_dim(module))
123
124
125def _gate_lower_bound(module: nn.Module) -> Optional[float]:
126 """Return the lower bound used by the safe KDA gate parameterization."""
127 return getattr(module, "lower_bound", getattr(module, "gate_lower_bound", None))
128
129
130def _uses_safe_gate(module: nn.Module) -> bool:
131 """Return whether the layer enables the safe lower-bounded gate."""
132 return bool(getattr(module, "safe_gate", _gate_lower_bound(module) is not None))
133
134
135def _uses_short_conv(module: nn.Module) -> bool:
136 """Return whether Q/K/V pass through the layer's ShortConvolution modules."""
137 return bool(
138 getattr(
139 module,
140 "use_short_conv",
141 all(hasattr(module, name) for name in ("q_conv1d", "k_conv1d", "v_conv1d")),
142 )
143 )
144
145
146def _forget_gate_projection(module: nn.Module, hidden_states: torch.Tensor) -> torch.Tensor:
147 """Run the module-specific state-decay gate projection."""
148 if hasattr(module, "f_proj"):
149 return module.f_proj(hidden_states)
150 return module.f_b_proj(module.f_a_proj(hidden_states))
151
152
153def _output_gate_projection(module: nn.Module, hidden_states: torch.Tensor) -> torch.Tensor:
154 """Run the module-specific output-gate projection."""
155 if hasattr(module, "g_proj"):
156 return module.g_proj(hidden_states)
157 return module.g_b_proj(module.g_a_proj(hidden_states))
158
159
160def _format_layer_output(module: nn.Module, output: torch.Tensor):
161 """Preserve the original layer's tensor or three-tuple output contract."""
162 if _is_official_kimi_layer(module):
163 return output
164 return output, None, None
165
166
167def _global_peer_rank(cp_mesh: DeviceMesh, local_rank: int) -> int:
168 """Map one CP-local rank index to its global distributed rank."""
169 return int(cp_mesh.rank_list[local_rank])
170
171
172def _local_cp_tensor(tensor: torch.Tensor) -> torch.Tensor:
173 """Return the local sequence shard of a CP input tensor."""
174 if isinstance(tensor, DTensor):
175 return tensor.to_local()
176 return tensor
177
178
179def _all_to_all_previous_rank_halo(
180 tail: torch.Tensor,
181 cp_mesh: DeviceMesh,
182 cp_rank: int,
183 cp_size: int,
184) -> torch.Tensor:
185 """Send a causal-convolution halo only to the next CP rank."""
186 if cp_size == 1:
187 return torch.zeros_like(tail)
188
189 cp_group = cp_mesh.get_group()
190 group_ranks = tuple(int(rank) for rank in dist.get_process_group_ranks(cp_group))
191 rank_list = tuple(int(rank) for rank in cp_mesh.rank_list)
192 rank_to_group_index = {rank: index for index, rank in enumerate(group_ranks)}
193 halo_width = tail.shape[1]
194
195 input_splits = [0] * cp_size
196 exchange_input = tail.permute(1, 0, 2).contiguous()
197 if cp_rank < cp_size - 1:
198 input_splits[rank_to_group_index[rank_list[cp_rank + 1]]] = halo_width
199 else:
200 exchange_input = exchange_input[:0]
201
202 output_splits = [0] * cp_size
203 if cp_rank > 0:
204 output_splits[rank_to_group_index[rank_list[cp_rank - 1]]] = halo_width
205
206 exchange_output = communication.differentiable_all_to_all_single(
207 exchange_input,
208 input_splits,
209 output_splits,
210 group=cp_group,
211 )
212 if cp_rank == 0:
213 return torch.zeros_like(tail) + exchange_output.sum().to(tail.dtype) * 0
214 return exchange_output.permute(1, 0, 2).contiguous()
215
216
217def _run_causal_short_conv(
218 tensor: torch.Tensor,
219 convolution: nn.Conv1d,
220 halo: Optional[torch.Tensor] = None,
221) -> torch.Tensor:
222 """Apply one local ShortConv, optionally with a preceding-rank halo."""
223 if halo is None:
224 conv_input = tensor.transpose(1, 2)
225 padding = convolution.padding
226 else:
227 conv_input = torch.cat((halo, tensor), dim=1).transpose(1, 2)
228 padding = 0
229 output = F.conv1d( # pylint: disable=not-callable
230 input=conv_input,
231 weight=convolution.weight,
232 bias=convolution.bias,
233 stride=convolution.stride,
234 padding=padding,
235 dilation=convolution.dilation,
236 groups=convolution.groups,
237 )
238 if halo is None:
239 output = output[:, :, : tensor.shape[1]]
240 return F.silu(output).transpose(1, 2)
241
242
243def _causal_short_convs_with_cp_halo(
244 projected: tuple[torch.Tensor, torch.Tensor, torch.Tensor],
245 convolutions: tuple[nn.Conv1d, nn.Conv1d, nn.Conv1d],
246 cp_mesh: DeviceMesh,
247 cp_rank: int,
248 cp_size: int,
249) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
250 """Run three causal depthwise ShortConvs with one packed halo exchange."""
251 halo_widths = {
252 (conv.kernel_size[0] - 1) * conv.dilation[0] for conv in convolutions
253 }
254 if len(halo_widths) != 1:
255 raise ValueError("KDA P2P requires Q/K/V ShortConv halo widths to match.")
256 halo_width = halo_widths.pop()
257 if halo_width == 0 or cp_size == 1:
258 return tuple(
259 _run_causal_short_conv(tensor, convolution)
260 for tensor, convolution in zip(projected, convolutions)
261 )
262 if projected[0].shape[1] < halo_width:
263 raise ValueError(
264 "KDA P2P ShortConv requires local_seq_len >= halo width, got "
265 f"{projected[0].shape[1]} < {halo_width}."
266 )
267
268 channel_sizes = [tensor.shape[-1] for tensor in projected]
269 packed_tail = torch.cat(
270 [tensor[:, -halo_width:, :] for tensor in projected],
271 dim=-1,
272 ).contiguous()
273 packed_halo = _all_to_all_previous_rank_halo(
274 packed_tail,
275 cp_mesh,
276 cp_rank,
277 cp_size,
278 )
279 halos = torch.split(packed_halo, channel_sizes, dim=-1)
280 return tuple(
281 _run_causal_short_conv(tensor, convolution, halo)
282 for tensor, halo, convolution in zip(projected, halos, convolutions)
283 )
284
285
286def _slice_local_heads(
287 tensor: torch.Tensor,
288 cp_rank: int,
289 cp_size: int,
290) -> torch.Tensor:
291 """Return the current CP rank's contiguous head slice."""
292 if tensor.shape[0] % cp_size != 0:
293 raise ValueError(
294 f"KDA head dimension {tensor.shape[0]} must be divisible by "
295 f"cp_size {cp_size}."
296 )
297 local_heads = tensor.shape[0] // cp_size
298 return tensor.narrow(0, cp_rank * local_heads, local_heads)
299
300
301def _differentiable_all_to_all_shard(
302 tensor: torch.Tensor,
303 device_mesh: DeviceMesh,
304 *,
305 split_dim: int,
306 concat_dim: int,
307) -> torch.Tensor:
308 """Redistribute a local tensor from concat-dim shard to split-dim shard."""
309 split_count = device_mesh.size()
310 if split_count == 1:
311 return tensor
312
313 original_shape = tuple(tensor.shape)
314 if original_shape[split_dim] % split_count != 0:
315 raise ValueError(
316 f"KDA all-to-all split dimension {split_dim} with size "
317 f"{original_shape[split_dim]} must be divisible by cp_size "
318 f"{split_count}."
319 )
320 split_size = original_shape[split_dim] // split_count
321 final_shape = list(original_shape)
322 final_shape[split_dim] = split_size
323 final_shape[concat_dim] *= split_count
324
325 reshape_dims = list(original_shape)
326 reshape_dims[split_dim] = split_count
327 reshape_dims.insert(split_dim + 1, split_size)
328 permutation = list(range(len(reshape_dims)))
329 permutation.remove(split_dim)
330 permutation.insert(0, split_dim)
331 all_to_all_input = tensor.reshape(reshape_dims).permute(permutation).contiguous()
332 all_to_all_input = all_to_all_input.flatten(0, 1)
333
334 split_length = all_to_all_input.shape[0] // split_count
335 splits = [split_length] * split_count
336 output = communication.differentiable_all_to_all_single(
337 all_to_all_input,
338 splits,
339 splits,
340 group=device_mesh.get_group(),
341 )
342
343 output_reshape = list(output.shape)
344 output_reshape[0] = split_count
345 output_reshape.insert(1, output.shape[0] // split_count)
346 permutation = list(range(len(output_reshape)))
347 rank_dimension = permutation.pop(0)
348 permutation.insert(concat_dim, rank_dimension)
349 output = output.reshape(output_reshape).permute(permutation).contiguous()
350
351 output_shape = list(output.shape)
352 output_shape[concat_dim] *= output_shape[concat_dim + 1]
353 output_shape.pop(concat_dim + 1)
354 return output.reshape(output_shape).view(final_shape)
355
356
357class _RecvKDAInitialState(torch.autograd.Function):
358 """Receive a KDA initial state and return its gradient to the sender."""
359
360 @staticmethod
361 def forward( # pylint: disable=arguments-differ
362 ctx: Any,
363 anchor: torch.Tensor,
364 cp_group: Any,
365 prev_rank: int,
366 state_shape: tuple[int, ...],
367 ) -> torch.Tensor:
368 """Receive the initial recurrent state from the previous CP rank.
369
370 Args:
371 ctx: Autograd context holding tensors and communication metadata.
372 anchor: Tensor supplying the receive device and autograd dependency.
373 cp_group: Precreated chronological CP process group.
374 prev_rank: Global rank of the preceding sequence partition.
375 state_shape: Shape of the FP32 recurrent state.
376 """
377 state = torch.empty(state_shape, device=anchor.device, dtype=torch.float32)
378 dist.recv(state, src=prev_rank, group=cp_group)
379 ctx.cp_group = cp_group
380 ctx.prev_rank = prev_rank
381 return state
382
383 @staticmethod
384 def backward(
385 ctx: Any,
386 grad_state: Optional[torch.Tensor],
387 ) -> tuple[None, None, None, None]:
388 """Send the accumulated initial-state gradient to the previous rank.
389
390 Args:
391 ctx: Autograd context holding tensors and communication metadata.
392 grad_state: Gradient of the received initial state.
393 """
394 if grad_state is None:
395 raise RuntimeError("KDA P2P backward is missing the initial-state gradient.")
396 dist.send(grad_state.contiguous(), dst=ctx.prev_rank, group=ctx.cp_group)
397 return None, None, None, None
398
399
400class _SendKDAFinalState(torch.autograd.Function):
401 """Send a KDA final state and receive its gradient during backward."""
402
403 @staticmethod
404 def forward( # pylint: disable=arguments-differ
405 ctx: Any,
406 final_state: torch.Tensor,
407 cp_group: Any,
408 next_rank: int,
409 ) -> torch.Tensor:
410 """Send the final recurrent state to the next CP rank.
411
412 Args:
413 ctx: Autograd context holding tensors and communication metadata.
414 final_state: Outgoing recurrent state.
415 cp_group: Precreated chronological CP process group.
416 next_rank: Global rank of the following sequence partition.
417 """
418 dist.send(final_state.contiguous(), dst=next_rank, group=cp_group)
419 ctx.cp_group = cp_group
420 ctx.next_rank = next_rank
421 ctx.state_shape = tuple(final_state.shape)
422 ctx.state_dtype = final_state.dtype
423 return final_state.new_zeros(())
424
425 @staticmethod
426 def backward(
427 ctx: Any,
428 grad_token: torch.Tensor,
429 ) -> tuple[torch.Tensor, None, None]:
430 """Receive the final-state gradient from the next rank.
431
432 Args:
433 ctx: Autograd context holding tensors and communication metadata.
434 grad_token: Gradient token supplying the receive device.
435 """
436 grad_state = torch.empty(
437 ctx.state_shape,
438 device=grad_token.device,
439 dtype=ctx.state_dtype,
440 )
441 dist.recv(grad_state, src=ctx.next_rank, group=ctx.cp_group)
442 return grad_state, None, None
443
444
445def _eager_kda_state_p2p(
446 query: torch.Tensor,
447 key: torch.Tensor,
448 value: torch.Tensor,
449 gate: torch.Tensor,
450 beta: torch.Tensor,
451 *,
452 a_log: torch.Tensor,
453 dt_bias: torch.Tensor,
454 scale: Optional[float],
455 lower_bound: float,
456 chunk_size: int,
457 cp_group: Any,
458 prev_rank: int,
459 next_rank: int,
460 cp_rank: int,
461 cp_size: int,
462) -> torch.Tensor:
463 """Run the differentiable Torch reference for affine state-P2P KDA."""
464 if cp_size == 1:
465 return _run_local_kda(
466 query,
467 key,
468 value,
469 gate,
470 beta,
471 a_log=a_log,
472 dt_bias=dt_bias,
473 scale=scale,
474 lower_bound=lower_bound,
475 chunk_size=chunk_size,
476 safe_gate=True,
477 backend="eager",
478 )
479
480 state_ext = None
481 transition = None
482 if cp_rank < cp_size - 1:
483 state_ext, transition = torch_kda_state_summary(
484 query,
485 key,
486 value,
487 gate,
488 beta,
489 a_log=a_log,
490 dt_bias=dt_bias,
491 scale=scale,
492 use_qk_l2norm_in_kernel=True,
493 use_gate_in_kernel=True,
494 use_beta_sigmoid_in_kernel=True,
495 lower_bound=lower_bound,
496 chunk_size=chunk_size,
497 )
498
499 initial_state = None
500 if cp_rank > 0:
501 state_shape = (
502 query.shape[0],
503 value.shape[2],
504 query.shape[-1],
505 value.shape[-1],
506 )
507 initial_state = _RecvKDAInitialState.apply(
508 query,
509 cp_group,
510 prev_rank,
511 state_shape,
512 )
513
514 send_token = None
515 if cp_rank < cp_size - 1:
516 final_state = torch_apply_kda_state_summary(
517 state_ext,
518 transition,
519 initial_state,
520 )
521 send_token = _SendKDAFinalState.apply(final_state, cp_group, next_rank)
522
523 output, _ = chunk_kda(
524 query,
525 key,
526 value,
527 gate,
528 beta,
529 a_log=a_log,
530 dt_bias=dt_bias,
531 scale=scale,
532 initial_state=initial_state,
533 output_final_state=False,
534 use_qk_l2norm_in_kernel=True,
535 use_gate_in_kernel=True,
536 use_beta_sigmoid_in_kernel=True,
537 lower_bound=lower_bound,
538 chunk_size=chunk_size,
539 backend="eager",
540 )
541 if send_token is not None:
542 output = output + send_token.to(output.dtype) * 0
543 return output
544
545
546def _validate_projected_inputs(
547 ulysses_degree: int,
548 query: torch.Tensor,
549 key: torch.Tensor,
550 value: torch.Tensor,
551 gate: torch.Tensor,
552 beta: torch.Tensor,
553 a_log: torch.Tensor,
554 dt_bias: torch.Tensor,
555) -> None:
556 """Validate the projected-tensor contract and Ulysses divisibility."""
557 if (query.dim(), key.dim(), value.dim(), gate.dim()) != (4, 4, 4, 4):
558 raise ValueError("query, key, value, and gate must be rank-4 tensors.")
559 if beta.dim() != 3:
560 raise ValueError("beta must be a rank-3 tensor.")
561 if query.shape != key.shape:
562 raise ValueError("query and key must have identical shapes.")
563
564 batch_size, local_seq_len, num_k_heads, k_head_dim = query.shape
565 expected_prefix = (batch_size, local_seq_len)
566 if value.shape[:2] != expected_prefix:
567 raise ValueError("value batch and sequence dimensions must match query.")
568 num_v_heads = value.shape[2]
569 if gate.shape != (batch_size, local_seq_len, num_v_heads, k_head_dim):
570 raise ValueError(
571 "gate must have shape [B, S_local, num_v_heads, k_head_dim]."
572 )
573 if beta.shape != (batch_size, local_seq_len, num_v_heads):
574 raise ValueError("beta must have shape [B, S_local, num_v_heads].")
575 if num_v_heads % num_k_heads != 0:
576 raise ValueError("num_v_heads must be divisible by num_k_heads.")
577 if num_k_heads % ulysses_degree != 0:
578 raise ValueError(
579 f"KDA num_k_heads ({num_k_heads}) must be divisible by "
580 f"cp_size ({ulysses_degree}) for Ulysses CP."
581 )
582 if num_v_heads % ulysses_degree != 0:
583 raise ValueError(
584 f"KDA num_v_heads ({num_v_heads}) must be divisible by "
585 f"cp_size ({ulysses_degree}) for Ulysses CP."
586 )
587 if a_log.numel() != num_v_heads:
588 raise ValueError(
589 f"a_log must contain {num_v_heads} elements, got {a_log.numel()}."
590 )
591 if dt_bias.numel() != num_v_heads * k_head_dim:
592 raise ValueError(
593 f"dt_bias must contain {num_v_heads * k_head_dim} elements, "
594 f"got {dt_bias.numel()}."
595 )
596
597
598class KimiDeltaAttentionUlyssesCP(nn.Module):
599 """Run KDA on sequence-sharded projected tensors with Ulysses all-to-all.
600
601 The input and output layout is token-first with a local sequence shard:
602
603 ``[B, S/P, H, D] -> [B, S, H/P, D] -> [B, S/P, H, D]``.
604
605 This first-stage interface intentionally starts after projections and
606 ShortConv and ends before the output norm/gate/projection. It is therefore
607 independent of one particular Kimi K3 model class. Training uses a zero
608 initial recurrent state and does not materialize a final state.
609 """
610
611 def __init__(
612 self,
613 device_mesh: DeviceMesh,
614 *,
615 chunk_size: int = 64,
616 lower_bound: float = -5.0,
617 safe_gate: bool = True,
618 backend: str = "eager",
619 ) -> None:
620 """Initialize the KDA Ulysses executor for one 1-D CP mesh."""
621 super().__init__()
622 if chunk_size <= 0:
623 raise ValueError(f"chunk_size must be positive, got {chunk_size}.")
624 if lower_bound >= 0:
625 raise ValueError(f"lower_bound must be negative, got {lower_bound}.")
626 backend = backend.lower()
627 if backend not in _KDA_BACKENDS:
628 raise ValueError(
629 f"unsupported KDA backend {backend!r}; "
630 f"expected one of {sorted(_KDA_BACKENDS)}."
631 )
632 self.cp_mesh = _ensure_1d(device_mesh)
633 self.cp_size = self.cp_mesh.size()
634 self.cp_rank = self.cp_mesh.get_local_rank()
635 self.chunk_size = chunk_size
636 self.lower_bound = lower_bound
637 self.safe_gate = safe_gate
638 self.backend = backend
639 self.seq_dim = 1
640 self.head_dim = 2
641
642 def _validate_inputs(
643 self, query: torch.Tensor, key: torch.Tensor, value: torch.Tensor,
644 gate: torch.Tensor, beta: torch.Tensor, a_log: torch.Tensor, dt_bias: torch.Tensor,
645 ) -> None:
646 """Validate the projected contract for this Ulysses degree."""
647 _validate_projected_inputs(self.cp_size, query, key, value, gate, beta, a_log, dt_bias)
648
649 def _seq_to_head(self, tensor: torch.Tensor) -> torch.Tensor:
650 """Redistribute a local sequence shard into a local head shard."""
651 return _differentiable_all_to_all_shard(
652 tensor,
653 self.cp_mesh,
654 split_dim=self.head_dim,
655 concat_dim=self.seq_dim,
656 )
657
658 def _head_to_seq(self, tensor: torch.Tensor) -> torch.Tensor:
659 """Redistribute a local head shard back into a local sequence shard."""
660 return _differentiable_all_to_all_shard(
661 tensor,
662 self.cp_mesh,
663 split_dim=self.seq_dim,
664 concat_dim=self.head_dim,
665 )
666
667 def forward(
668 self,
669 query: torch.Tensor,
670 key: torch.Tensor,
671 value: torch.Tensor,
672 gate: torch.Tensor,
673 beta: torch.Tensor,
674 *,
675 a_log: torch.Tensor,
676 dt_bias: torch.Tensor,
677 scale: Optional[float] = None,
678 ) -> torch.Tensor:
679 """Run chunkwise KDA using full sequence and local heads.
680
681 Args:
682 query: Local query tensor [B,T,H,K].
683 key: Matching local key tensor.
684 value: Local value tensor [B,T,HV,V].
685 gate: Per-token gate logits [B,T,HV,K].
686 beta: Per-token beta logits [B,T,HV].
687 a_log: Decay parameter with one value per value head.
688 dt_bias: Learned bias with one value per gate channel.
689 scale: Optional attention scaling factor.
690 """
691 self._validate_inputs(query, key, value, gate, beta, a_log, dt_bias)
692 num_v_heads = value.shape[2]
693 k_head_dim = query.shape[-1]
694
695 query = self._seq_to_head(query)
696 key = self._seq_to_head(key)
697 value = self._seq_to_head(value)
698 gate = self._seq_to_head(gate)
699 beta = self._seq_to_head(beta)
700
701 local_a_log = _slice_local_heads(
702 a_log.reshape(num_v_heads),
703 self.cp_rank,
704 self.cp_size,
705 )
706 local_dt_bias = _slice_local_heads(
707 dt_bias.reshape(num_v_heads, k_head_dim),
708 self.cp_rank,
709 self.cp_size,
710 )
711 output = _run_local_kda(
712 query,
713 key,
714 value,
715 gate,
716 beta,
717 a_log=local_a_log,
718 dt_bias=local_dt_bias,
719 scale=scale,
720 lower_bound=self.lower_bound,
721 chunk_size=self.chunk_size,
722 safe_gate=self.safe_gate,
723 backend=self.backend,
724 )
725 return self._head_to_seq(output)
726
727
728class _KDAStateCP(nn.Module):
729 """Run local KDA with the configured P2P or gathered state exchange."""
730
731 def __init__(
732 self,
733 device_mesh: DeviceMesh,
734 *,
735 chunk_size: int = 64,
736 lower_bound: float = -5.0,
737 safe_gate: bool = True,
738 backend: str = "eager",
739 state_cp_method: str = "p2p",
740 allgather_group_size: int = 1,
741 ) -> None:
742 """Initialize local KDA and its state exchange on one chronological axis."""
743 super().__init__()
744 if chunk_size <= 0:
745 raise ValueError(f"chunk_size must be positive, got {chunk_size}.")
746 backend = backend.lower()
747 if backend not in _KDA_BACKENDS:
748 raise ValueError(
749 f"unsupported KDA backend {backend!r}; "
750 f"expected one of {sorted(_KDA_BACKENDS)}."
751 )
752 self.cp_mesh = _ensure_1d(device_mesh)
753 self.cp_size = self.cp_mesh.size()
754 self.cp_rank = self.cp_mesh.get_local_rank()
755 self.cp_group = self.cp_mesh.get_group()
756 if backend != "triton" and state_cp_method != "p2p":
757 raise ValueError("KDA AllGather state exchange requires backend='triton'.")
758 self.state_cp = build_kda_state_cp(self.cp_mesh, state_cp_method, allgather_group_size)
759 self.prev_rank = _global_peer_rank(
760 self.cp_mesh,
761 max(self.cp_rank - 1, 0),
762 )
763 self.next_rank = _global_peer_rank(
764 self.cp_mesh,
765 min(self.cp_rank + 1, self.cp_size - 1),
766 )
767 self.chunk_size = chunk_size
768 self.lower_bound = lower_bound
769 self.safe_gate = safe_gate
770 self.backend = backend
771
772 def forward(
773 self,
774 query: torch.Tensor,
775 key: torch.Tensor,
776 value: torch.Tensor,
777 gate: torch.Tensor,
778 beta: torch.Tensor,
779 *,
780 a_log: torch.Tensor,
781 dt_bias: torch.Tensor,
782 scale: Optional[float] = None,
783 ) -> torch.Tensor:
784 """Run one local KDA segment and propagate its recurrent state.
785
786 Args:
787 query: Local query tensor [B,T,H,K].
788 key: Matching local key tensor.
789 value: Local value tensor [B,T,HV,V].
790 gate: Per-token gate logits [B,T,HV,K].
791 beta: Per-token beta logits [B,T,HV].
792 a_log: Decay parameter with one value per value head.
793 dt_bias: Learned bias with one value per gate channel.
794 scale: Optional attention scaling factor.
795 """
796 if self.cp_size == 1:
797 return _run_local_kda(
798 query,
799 key,
800 value,
801 gate,
802 beta,
803 a_log=a_log,
804 dt_bias=dt_bias,
805 scale=scale,
806 lower_bound=self.lower_bound,
807 chunk_size=self.chunk_size,
808 safe_gate=self.safe_gate,
809 backend=self.backend,
810 )
811 if self.backend == "eager":
812 return _eager_kda_state_p2p(
813 query,
814 key,
815 value,
816 gate,
817 beta,
818 a_log=a_log,
819 dt_bias=dt_bias,
820 scale=scale,
821 lower_bound=self.lower_bound,
822 chunk_size=self.chunk_size,
823 cp_group=self.cp_group,
824 prev_rank=self.prev_rank,
825 next_rank=self.next_rank,
826 cp_rank=self.cp_rank,
827 cp_size=self.cp_size,
828 )
829
830 from hyper_parallel.components.functional.kimi_delta_attention import ( # pylint: disable=import-outside-toplevel
831 fused_chunk_kda_p2p,
832 )
833 return fused_chunk_kda_p2p(
834 query,
835 key,
836 value,
837 gate,
838 beta,
839 a_log=a_log.float(),
840 dt_bias=dt_bias.float(),
841 cp_group=self.cp_group,
842 prev_rank=self.prev_rank,
843 next_rank=self.next_rank,
844 cp_rank=self.cp_rank,
845 cp_size=self.cp_size,
846 scale=scale,
847 lower_bound=self.lower_bound,
848 chunk_size=self.chunk_size,
849 safe_gate=self.safe_gate,
850 state_cp=self.state_cp,
851 )
852
853
854class KimiDeltaAttentionP2PCP(_KDAStateCP):
855 """Preserve the public projected-P2P type while sharing state execution.
856
857 Inherited defaults select P2P. Keeping a named subclass preserves the old
858 import and type name without duplicating the common implementation.
859 New hybrid configurations use KimiDeltaAttentionCoreCP.
860 """
861
862
863def _validate_layer_short_conv(module: nn.Module, mode: str) -> None:
864 """Check the shared depthwise-convolution contract before building execution."""
865 if _uses_short_conv(module):
866 for name in ("q_conv1d", "k_conv1d", "v_conv1d"):
867 if not hasattr(module, name):
868 raise TypeError(f"Kimi K3 layer is missing {name}.")
869 convolution = getattr(module, name)
870 if convolution.stride != (1,) or convolution.dilation != (1,):
871 raise ValueError(
872 f"Kimi K3 {mode} CP supports ShortConv stride=1 and "
873 "dilation=1 only."
874 )
875 if convolution.groups != convolution.in_channels or convolution.out_channels != convolution.in_channels:
876 raise ValueError(f"Kimi K3 {mode} CP expects depthwise ShortConv with equal input/output channels.")
877 if convolution.padding != (convolution.kernel_size[0] - 1,) or convolution.padding_mode != "zeros":
878 raise ValueError(f"Kimi K3 {mode} CP expects causal zero-padded ShortConv.")
879 if getattr(convolution, "activation", None) not in ("silu", "swish"):
880 raise ValueError(
881 f"Kimi K3 {mode} CP expects ShortConv SiLU activation."
882 )
883
884
885class KimiDeltaAttentionLayerUlyssesCP(KimiDeltaAttentionUlyssesCP):
886 """Wrap a training-time Kimi K3 attention layer with Ulysses CP.
887
888 The wrapped module keeps ownership of all model parameters. Its local
889 sequence shard is projected first, then Q/K/V, per-dimension gate logits,
890 and beta logits are packed into one sequence-to-head all-to-all. Each CP
891 rank runs the wrapped layer's ShortConv weights and fused KDA core for its
892 local head shard and the full sequence. The reverse all-to-all restores a
893 local sequence shard before the original output gate, norm, and projection.
894
895 This first integration targets dense training batches. Inference cache and
896 padded or packed variable-length sequences require separate CP metadata
897 handling and are rejected explicitly.
898 """
899
900 def __init__(
901 self,
902 module: nn.Module,
903 device_mesh: DeviceMesh,
904 *,
905 chunk_size: int = 64,
906 backend: str = "eager",
907 ) -> None:
908 """Initialize the full-layer adapter without replacing its parameters."""
909 lower_bound = _gate_lower_bound(module)
910 if lower_bound is None:
911 raise ValueError(
912 "Kimi K3 Ulysses CP requires the lower-bounded gate parameterization."
913 )
914 super().__init__(
915 device_mesh,
916 chunk_size=chunk_size,
917 lower_bound=float(lower_bound),
918 safe_gate=_uses_safe_gate(module),
919 backend=backend,
920 )
921 self.module = module
922 self._validate_module()
923
924 def _validate_module(self) -> None:
925 """Validate the first-stage KimiDeltaAttention module contract."""
926 required_attributes = (
927 "q_proj",
928 "k_proj",
929 "v_proj",
930 "b_proj",
931 "o_norm",
932 "o_proj",
933 "A_log",
934 "dt_bias",
935 "num_heads",
936 "head_k_dim",
937 )
938 missing = [name for name in required_attributes if not hasattr(self.module, name)]
939 if missing:
940 raise TypeError(
941 "Kimi K3 layer is missing required attributes: "
942 + ", ".join(missing)
943 )
944 if self.module.num_heads % self.cp_size != 0:
945 raise ValueError(
946 f"KDA num_heads ({self.module.num_heads}) must be divisible by "
947 f"cp_size ({self.cp_size})."
948 )
949 num_v_heads = _num_value_heads(self.module)
950 if num_v_heads % self.cp_size != 0:
951 raise ValueError(
952 f"KDA num_v_heads ({num_v_heads}) must be divisible "
953 f"by cp_size ({self.cp_size})."
954 )
955 if num_v_heads % self.module.num_heads != 0:
956 raise ValueError("KDA num_v_heads must be divisible by num_heads.")
957 if not (hasattr(self.module, "f_proj") or _is_official_kimi_layer(self.module)):
958 raise TypeError("Kimi K3 layer is missing its forget-gate projection.")
959 if not (
960 hasattr(self.module, "g_proj")
961 or all(hasattr(self.module, name) for name in ("g_a_proj", "g_b_proj"))
962 ):
963 raise TypeError("Kimi K3 layer is missing its output-gate projection.")
964 if getattr(self.module, "allow_neg_eigval", False):
965 raise NotImplementedError(
966 "Kimi K3 Ulysses CP does not yet support allow_neg_eigval=True."
967 )
968
969 _validate_layer_short_conv(self.module, "Ulysses")
970
971 def _pack_projected_inputs(
972 self,
973 query: torch.Tensor,
974 key: torch.Tensor,
975 value: torch.Tensor,
976 gate: torch.Tensor,
977 beta: torch.Tensor,
978 ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
979 """Pack all projected paths into one sequence-to-head all-to-all."""
980 if self.cp_size == 1:
981 return query, key, value, gate, beta
982
983 base = self.module
984 split_sizes = (
985 _key_projection_dim(base) // self.cp_size,
986 _key_projection_dim(base) // self.cp_size,
987 _value_projection_dim(base) // self.cp_size,
988 _num_value_heads(base) * base.head_k_dim // self.cp_size,
989 _num_value_heads(base) // self.cp_size,
990 )
991 projected = (query, key, value, gate, beta)
992 chunks = [torch.split(tensor, size, dim=-1) for tensor, size in zip(projected, split_sizes)]
993 rank_major = [
994 torch.cat(parts, dim=-1)
995 for parts in zip(*chunks)
996 ]
997 packed = self._seq_to_head(torch.cat(rank_major, dim=-1).contiguous())
998 return torch.split(packed, split_sizes, dim=-1)
999
1000 def _local_short_conv(
1001 self,
1002 projected: torch.Tensor,
1003 convolution: nn.Conv1d,
1004 ) -> torch.Tensor:
1005 """Run one wrapped ShortConv on this rank's contiguous channel slice."""
1006 local_channels = projected.shape[-1]
1007 channel_start = self.cp_rank * local_channels
1008 weight = convolution.weight.narrow(0, channel_start, local_channels)
1009 bias = convolution.bias
1010 if bias is not None:
1011 bias = bias.narrow(0, channel_start, local_channels)
1012 output = F.conv1d( # pylint: disable=not-callable
1013 projected.transpose(1, 2),
1014 weight,
1015 bias,
1016 stride=convolution.stride,
1017 padding=convolution.padding,
1018 dilation=convolution.dilation,
1019 groups=local_channels,
1020 )
1021 output = output[:, :, : projected.shape[1]].transpose(1, 2)
1022 return F.silu(output)
1023
1024 def forward( # pylint: disable=arguments-renamed
1025 self,
1026 hidden_states: torch.Tensor,
1027 attention_mask: Optional[torch.Tensor] = None,
1028 past_key_values: Optional[Any] = None,
1029 use_cache: bool = False,
1030 output_attentions: bool = False,
1031 **kwargs: Any,
1032 ) -> tuple[torch.Tensor, None, None]:
1033 """Run the real Kimi layer using KDA and Ulysses CP.
1034
1035 Args:
1036 hidden_states: Local sequence shard [B,T,hidden_size].
1037 attention_mask: Optional dense mask; padding is unsupported.
1038 past_key_values: Recurrent inference cache, currently unsupported.
1039 use_cache: Must be False for this training adapter.
1040 output_attentions: Must be False; KDA does not materialize attention weights.
1041 kwargs: Additional model arguments; packed sequences are unsupported.
1042 """
1043 if not self.module.training:
1044 raise NotImplementedError("Kimi K3 Ulysses CP currently supports training only.")
1045 if past_key_values is not None or use_cache:
1046 raise NotImplementedError("Kimi K3 Ulysses CP does not support recurrent cache.")
1047 if output_attentions:
1048 raise NotImplementedError("KDA does not materialize attention weights.")
1049 if kwargs.get("cu_seqlens") is not None:
1050 raise NotImplementedError("Packed variable-length KDA CP is not implemented.")
1051 if attention_mask is not None and not bool(attention_mask.bool().all().item()):
1052 raise NotImplementedError("Padded KDA CP batches are not implemented.")
1053
1054 hidden_states = _local_cp_tensor(hidden_states)
1055 base = self.module
1056 batch_size, local_seq_len, _ = hidden_states.shape
1057 query = base.q_proj(hidden_states)
1058 key = base.k_proj(hidden_states)
1059 value = base.v_proj(hidden_states)
1060 gate = _forget_gate_projection(base, hidden_states)
1061 beta = base.b_proj(hidden_states)
1062 output_gate = _output_gate_projection(base, hidden_states).reshape(
1063 batch_size,
1064 local_seq_len,
1065 _num_value_heads(base),
1066 _value_head_dim(base),
1067 )
1068
1069 query, key, value, gate, beta = self._pack_projected_inputs(query, key, value, gate, beta)
1070 if _uses_short_conv(base):
1071 query = self._local_short_conv(query, base.q_conv1d)
1072 key = self._local_short_conv(key, base.k_conv1d)
1073 value = self._local_short_conv(value, base.v_conv1d)
1074 else:
1075 query, key, value = (F.silu(tensor) for tensor in (query, key, value))
1076
1077 full_seq_len = query.shape[1]
1078 local_k_heads = base.num_heads // self.cp_size
1079 local_v_heads = _num_value_heads(base) // self.cp_size
1080 query = query.reshape(
1081 batch_size, full_seq_len, local_k_heads, base.head_k_dim
1082 )
1083 key = key.reshape(batch_size, full_seq_len, local_k_heads, base.head_k_dim)
1084 value = value.reshape(
1085 batch_size, full_seq_len, local_v_heads, _value_head_dim(base)
1086 )
1087 gate = gate.reshape(
1088 batch_size, full_seq_len, local_v_heads, base.head_k_dim
1089 )
1090 beta = beta.reshape(batch_size, full_seq_len, local_v_heads)
1091
1092 local_a_log = _slice_local_heads(
1093 base.A_log.reshape(_num_value_heads(base)),
1094 self.cp_rank,
1095 self.cp_size,
1096 )
1097 local_dt_bias = _slice_local_heads(
1098 base.dt_bias.reshape(_num_value_heads(base), base.head_k_dim),
1099 self.cp_rank,
1100 self.cp_size,
1101 )
1102 output = _run_local_kda(
1103 query,
1104 key,
1105 value,
1106 gate,
1107 beta,
1108 a_log=local_a_log,
1109 dt_bias=local_dt_bias,
1110 scale=None,
1111 lower_bound=self.lower_bound,
1112 chunk_size=self.chunk_size,
1113 safe_gate=self.safe_gate,
1114 backend=self.backend,
1115 )
1116 output = self._head_to_seq(output)
1117 output = base.o_norm(output, output_gate)
1118 output = output.reshape(
1119 batch_size, local_seq_len, _value_projection_dim(base)
1120 )
1121 output = base.o_proj(output)
1122 return _format_layer_output(base, output)
1123
1124
1125def _validate_cp_degrees(cp_size: int, ulysses_degree: int, allgather_group_size: int) -> None:
1126 """Validate both split extents before head division or process-group creation."""
1127 if (not isinstance(ulysses_degree, int) or isinstance(ulysses_degree, bool)
1128 or ulysses_degree < 1 or cp_size % ulysses_degree):
1129 raise ValueError("ulysses_degree must be a positive integer dividing the CP size.")
1130 if (not isinstance(allgather_group_size, int) or isinstance(allgather_group_size, bool)
1131 or allgather_group_size < 1 or (cp_size // ulysses_degree) % allgather_group_size):
1132 raise ValueError("allgather_group_size must be a positive integer dividing the state CP size.")
1133
1134
1135class KimiDeltaAttentionLayerCP(nn.Module):
1136 """Project local tokens, exchange convolution halos, and invoke the configured CP core."""
1137
1138 def __init__(
1139 self,
1140 module: nn.Module,
1141 device_mesh: DeviceMesh,
1142 *,
1143 chunk_size: int = 64,
1144 backend: str = "eager",
1145 ulysses_degree: int = 1,
1146 state_cp_method: str = "p2p",
1147 allgather_group_size: int = 1,
1148 ) -> None:
1149 """Keep layer ownership and full-CP halo metadata separate from the core."""
1150 super().__init__()
1151 lower_bound = _gate_lower_bound(module)
1152 if lower_bound is None:
1153 raise ValueError("Kimi K3 CP requires the lower-bounded gate parameterization.")
1154 self.cp_mesh = _ensure_1d(device_mesh)
1155 self.cp_rank, self.cp_size = self.cp_mesh.get_local_rank(), self.cp_mesh.size()
1156 self.module, self.backend = module, backend.lower()
1157 self._validate_module()
1158 _validate_cp_degrees(self.cp_size, ulysses_degree, allgather_group_size)
1159 if module.num_heads % ulysses_degree or _num_value_heads(module) % ulysses_degree:
1160 raise ValueError("KDA query and value heads must both be divisible by ulysses_degree.")
1161 self.core = KimiDeltaAttentionCoreCP(
1162 device_mesh, ulysses_degree=ulysses_degree, chunk_size=chunk_size,
1163 lower_bound=float(lower_bound), safe_gate=_uses_safe_gate(module), backend=backend,
1164 state_cp_method=state_cp_method, allgather_group_size=allgather_group_size)
1165
1166 def _validate_module(self) -> None:
1167 """Validate the KimiDeltaAttention contract used by state CP."""
1168 required_attributes = (
1169 "q_proj",
1170 "k_proj",
1171 "v_proj",
1172 "b_proj",
1173 "o_norm",
1174 "o_proj",
1175 "A_log",
1176 "dt_bias",
1177 "num_heads",
1178 "head_k_dim",
1179 )
1180 missing = [name for name in required_attributes if not hasattr(self.module, name)]
1181 if missing:
1182 raise TypeError(
1183 "Kimi K3 layer is missing required attributes: "
1184 + ", ".join(missing)
1185 )
1186 num_v_heads = _num_value_heads(self.module)
1187 if num_v_heads % self.module.num_heads:
1188 raise ValueError("KDA num_v_heads must be divisible by num_heads.")
1189 if self.backend == "triton" and (
1190 self.module.head_k_dim != 128 or _value_head_dim(self.module) != 128
1191 ):
1192 raise NotImplementedError(
1193 "Kimi K3 CP currently requires key/value head dimensions 128."
1194 )
1195 if getattr(self.module, "allow_neg_eigval", False):
1196 raise NotImplementedError(
1197 "Kimi K3 CP does not support allow_neg_eigval=True."
1198 )
1199 if not (hasattr(self.module, "f_proj") or _is_official_kimi_layer(self.module)):
1200 raise TypeError("Kimi K3 layer is missing its forget-gate projection.")
1201 if not (
1202 hasattr(self.module, "g_proj")
1203 or all(hasattr(self.module, name) for name in ("g_a_proj", "g_b_proj"))
1204 ):
1205 raise TypeError("Kimi K3 layer is missing its output-gate projection.")
1206 _validate_layer_short_conv(self.module, "state")
1207
1208 def forward( # pylint: disable=arguments-renamed
1209 self,
1210 hidden_states: torch.Tensor,
1211 attention_mask: Optional[torch.Tensor] = None,
1212 past_key_values: Optional[Any] = None,
1213 use_cache: bool = False,
1214 output_attentions: bool = False,
1215 **kwargs: Any,
1216 ) -> tuple[torch.Tensor, None, None]:
1217 """Run projections, full-CP halos, the configured CP core, and output transforms.
1218
1219 Args:
1220 hidden_states: Local sequence shard [B,T,hidden_size].
1221 attention_mask: Optional dense mask; padding is unsupported.
1222 past_key_values: Recurrent inference cache, currently unsupported.
1223 use_cache: Must be False for this training adapter.
1224 output_attentions: Must be False; KDA does not materialize attention weights.
1225 kwargs: Additional model arguments; packed sequences are unsupported.
1226 """
1227 if not self.module.training:
1228 raise NotImplementedError("Kimi K3 CP currently supports training only.")
1229 if past_key_values is not None or use_cache:
1230 raise NotImplementedError("Kimi K3 CP does not support recurrent cache.")
1231 if output_attentions:
1232 raise NotImplementedError("KDA does not materialize attention weights.")
1233 if kwargs.get("cu_seqlens") is not None:
1234 raise NotImplementedError("Packed variable-length KDA CP is not implemented.")
1235 if attention_mask is not None and not bool(attention_mask.bool().all().item()):
1236 raise NotImplementedError("Padded KDA CP batches are not implemented.")
1237
1238 hidden_states = _local_cp_tensor(hidden_states)
1239 base = self.module
1240 batch_size, local_seq_len, _ = hidden_states.shape
1241 query = base.q_proj(hidden_states)
1242 key = base.k_proj(hidden_states)
1243 value = base.v_proj(hidden_states)
1244 if _uses_short_conv(base):
1245 query, key, value = _causal_short_convs_with_cp_halo( # pylint: disable=unbalanced-tuple-unpacking
1246 (query, key, value),
1247 (base.q_conv1d, base.k_conv1d, base.v_conv1d),
1248 self.cp_mesh,
1249 self.cp_rank,
1250 self.cp_size,
1251 )
1252 else:
1253 query, key, value = (F.silu(tensor) for tensor in (query, key, value))
1254
1255 query = query.reshape(
1256 batch_size,
1257 local_seq_len,
1258 base.num_heads,
1259 base.head_k_dim,
1260 )
1261 key = key.reshape(
1262 batch_size,
1263 local_seq_len,
1264 base.num_heads,
1265 base.head_k_dim,
1266 )
1267 value = value.reshape(
1268 batch_size,
1269 local_seq_len,
1270 _num_value_heads(base),
1271 _value_head_dim(base),
1272 )
1273 gate = _forget_gate_projection(base, hidden_states).reshape(
1274 batch_size,
1275 local_seq_len,
1276 _num_value_heads(base),
1277 base.head_k_dim,
1278 )
1279 beta = base.b_proj(hidden_states).reshape(
1280 batch_size,
1281 local_seq_len,
1282 _num_value_heads(base),
1283 )
1284 output_gate = _output_gate_projection(base, hidden_states).reshape(
1285 batch_size,
1286 local_seq_len,
1287 _num_value_heads(base),
1288 _value_head_dim(base),
1289 )
1290
1291 output = self.core(
1292 query,
1293 key,
1294 value,
1295 gate,
1296 beta,
1297 a_log=base.A_log,
1298 dt_bias=base.dt_bias,
1299 )
1300 output = base.o_norm(output, output_gate)
1301 output = output.reshape(
1302 batch_size, local_seq_len, _value_projection_dim(base)
1303 )
1304 output = base.o_proj(output)
1305 return _format_layer_output(base, output)
1306
1307
1308class KimiDeltaAttentionCoreCP(nn.Module):
1309 """Compose optional Ulysses redistribution with local KDA and state exchange.
1310
1311 Inputs are projected Q/K/V/gate/beta after ShortConv. The layer adapter
1312 handles projections and full-sequence halo neighbors before calling here.
1313 """
1314
1315 def __init__(self, device_mesh: DeviceMesh, *, ulysses_degree: int = 1,
1316 chunk_size: int = 64, lower_bound: float = -5.0, safe_gate: bool = True,
1317 backend: str = "triton", state_cp_method: str = "p2p",
1318 allgather_group_size: int = 1) -> None:
1319 """Build all communication groups once, before forward or checkpoint replay."""
1320 super().__init__()
1321 device_mesh = _ensure_1d(device_mesh)
1322 if state_cp_method not in ("p2p", "allgather", "grouped_allgather_p2p"):
1323 raise ValueError("KDA requires a supported state_cp_method.")
1324 if backend.lower() != "triton" and state_cp_method != "p2p":
1325 raise ValueError("KDA AllGather states require backend='triton'.")
1326 _validate_cp_degrees(device_mesh.size(), ulysses_degree, allgather_group_size)
1327 if state_cp_method != "grouped_allgather_p2p" and allgather_group_size != 1:
1328 raise ValueError("allgather_group_size is only valid for grouped_allgather_p2p.")
1329 if lower_bound >= 0:
1330 raise ValueError("lower_bound must be negative.")
1331 self.ulysses_degree = ulysses_degree
1332 self.ulysses_mesh = None
1333 self.ulysses_rank = 0
1334 state_mesh = device_mesh
1335 if ulysses_degree > 1:
1336 hybrid = split_kda_mesh(device_mesh, ulysses_degree, ("kda_state", "kda_ulysses"))
1337 self.ulysses_mesh = hybrid["kda_ulysses"]
1338 self.ulysses_rank = self.ulysses_mesh.get_local_rank()
1339 state_mesh = hybrid["kda_state"]
1340 self.state = _KDAStateCP(
1341 state_mesh, chunk_size=chunk_size, lower_bound=lower_bound, safe_gate=safe_gate,
1342 backend=backend, state_cp_method=state_cp_method, allgather_group_size=allgather_group_size)
1343
1344 def forward(self, query: torch.Tensor, key: torch.Tensor, value: torch.Tensor,
1345 gate: torch.Tensor, beta: torch.Tensor, *, a_log: torch.Tensor,
1346 dt_bias: torch.Tensor, scale: float | None = None) -> torch.Tensor:
1347 """Run state CP with optional token/head redistribution.
1348
1349 Args:
1350 query: Projected query tensor [B,T,H,K].
1351 key: Matching local key tensor.
1352 value: Projected value tensor [B,T,HV,V].
1353 gate: Per-token gate logits [B,T,HV,K].
1354 beta: Per-token beta logits [B,T,HV].
1355 a_log: Learned decay parameter with one entry per value head.
1356 dt_bias: Learned gate bias with HV times K entries.
1357 scale: Attention scaling factor; None selects the local default.
1358
1359 Returns:
1360 Output restored to this rank's original token/head layout.
1361 """
1362 if self.ulysses_mesh is None:
1363 return self.state(query, key, value, gate, beta, a_log=a_log, dt_bias=dt_bias, scale=scale)
1364 _validate_projected_inputs(self.ulysses_degree, query, key, value, gate, beta, a_log, dt_bias)
1365 heads, key_dim = value.shape[2], query.shape[-1]
1366 inputs = self._exchange_inputs((query, key, value, gate, beta))
1367 local_a = _slice_local_heads(a_log.reshape(heads), self.ulysses_rank, self.ulysses_degree)
1368 local_bias = _slice_local_heads(dt_bias.reshape(heads, key_dim), self.ulysses_rank, self.ulysses_degree)
1369 output = self.state(*inputs, a_log=local_a, dt_bias=local_bias, scale=scale)
1370 return _differentiable_all_to_all_shard(output, self.ulysses_mesh, split_dim=1, concat_dim=2)
1371
1372 def _exchange_inputs(self, inputs: tuple[torch.Tensor, ...]) -> tuple[torch.Tensor, ...]:
1373 """Redistribute projected inputs using the established synchronous schedule."""
1374 return tuple(_differentiable_all_to_all_shard(tensor, self.ulysses_mesh, split_dim=2, concat_dim=1)
1375 for tensor in inputs)
1376
1377
1378class KimiDeltaAttentionLayerP2PCP(KimiDeltaAttentionLayerCP):
1379 """Preserve the public layer-P2P type while sharing layer execution.
1380
1381 Inherited defaults select P2P with no Ulysses. The subclass retains the old
1382 import and type name; new hybrid configurations use KimiDeltaAttentionLayerCP.
1383 """