|
21 | 21 | from ...configuration_utils import ConfigMixin, register_to_config |
22 | 22 | from ...loaders import PeftAdapterMixin |
23 | 23 | from ...utils import BaseOutput, apply_lora_scale, logging |
| 24 | +from .._modeling_parallel import ContextParallelInput, ContextParallelOutput |
24 | 25 | from ..attention import AttentionMixin, AttentionModuleMixin, FeedForward |
25 | 26 | from ..attention_dispatch import dispatch_attention_fn |
26 | 27 | from ..cache_utils import CacheMixin |
@@ -446,6 +447,36 @@ class MiniMaxH3Transformer3DModel(ModelMixin, ConfigMixin, AttentionMixin, PeftA |
446 | 447 | "audio_proj_out", |
447 | 448 | "rope", |
448 | 449 | ] |
| 450 | + # Context parallelism shards the packed sequence, so the split cannot happen on the inputs of `forward`: the rows |
| 451 | + # of the three modalities are scattered into the packed buffer with sequence-wide indices, which only address the |
| 452 | + # full sequence. The split therefore happens once the buffer is built, at the first block, and everything that is |
| 453 | + # indexed per row of the packed sequence is split alongside it: `adaln_indices` for the block stack, |
| 454 | + # `timestep_indices` for `norm_out`, and the two `rope` outputs, which carry one row of angles each. The two output |
| 455 | + # heads gather the sequence back, because `forward` then selects the video and audio rows with sequence-wide |
| 456 | + # indices as well. |
| 457 | + # |
| 458 | + # The packed sequence carries no padding, so its length is whatever the caller packed. Splitting it across the |
| 459 | + # context parallel region requires that length to be divisible by the region size — use `ulysses_anything` or |
| 460 | + # `ring_anything` for a layout that is not. |
| 461 | + _cp_plan = { |
| 462 | + "rope": { |
| 463 | + 0: ContextParallelInput(split_dim=0, expected_dims=2, split_output=True), |
| 464 | + 1: ContextParallelInput(split_dim=0, expected_dims=2, split_output=True), |
| 465 | + }, |
| 466 | + "transformer_blocks.0": { |
| 467 | + "hidden_states": ContextParallelInput(split_dim=1, expected_dims=3, split_output=False), |
| 468 | + }, |
| 469 | + # `hidden_states` is split once and then flows from block to block already sharded, but every block reads |
| 470 | + # `adaln_indices` from `forward`'s unsharded tensor, so each of them splits its own copy. |
| 471 | + "transformer_blocks.*": { |
| 472 | + "adaln_indices": ContextParallelInput(split_dim=0, expected_dims=1, split_output=False), |
| 473 | + }, |
| 474 | + "norm_out": { |
| 475 | + "timestep_indices": ContextParallelInput(split_dim=0, expected_dims=1, split_output=False), |
| 476 | + }, |
| 477 | + "proj_out": ContextParallelOutput(gather_dim=1, expected_dims=3), |
| 478 | + "audio_proj_out": ContextParallelOutput(gather_dim=1, expected_dims=3), |
| 479 | + } |
449 | 480 |
|
450 | 481 | @register_to_config |
451 | 482 | def __init__( |
|
0 commit comments