# Copyright 2025 Bytedance Ltd. and/or its affiliates. and the LlamaFactory team. # # This code is inspired by the Bytedance's verl library. # https://github.com/verl-project/verl/blob/77476af84cc074edf5a6437f8d5ea418d7a54916/verl/utils/ulysses.py # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from typing import Any, Optional import torch import torch.distributed as dist from torch import Tensor def all_to_all_tensor( local_input: Tensor, scatter_dim: int, gather_dim: int, group: Optional[dist.ProcessGroup] = None, ): seq_world_size = dist.get_world_size(group) input_list = [t.contiguous() for t in torch.tensor_split(local_input, seq_world_size, scatter_dim)] output_list = [torch.empty_like(input_list[0]) for _ in range(seq_world_size)] dist.all_to_all(output_list, input_list, group=group) return torch.cat(output_list, dim=gather_dim).contiguous() class SeqAllToAll4D(torch.autograd.Function): @staticmethod def forward( ctx: Any, group: dist.ProcessGroup, local_input: Tensor, scatter_dim: int, gather_dim: int, ) -> Tensor: ctx.group = group ctx.scatter_dim = scatter_dim ctx.gather_dim = gather_dim return all_to_all_tensor(local_input, scatter_dim, gather_dim, group) @staticmethod def backward(ctx: Any, *grad_output: Tensor) -> tuple[None, Tensor, None, None]: return ( None, all_to_all_tensor(grad_output[0], ctx.gather_dim, ctx.scatter_dim, ctx.group), None, None, )