Spaces:
Running on Zero
Running on Zero
| # Copyright 2024-2025 The Alibaba Wan Team Authors. All rights reserved. | |
| # Modified by Applied Intuition, Inc. in 2026. | |
| # SPDX-License-Identifier: Apache-2.0 | |
| import torch | |
| import torch.distributed as dist | |
| _sequence_parallel_group = None | |
| def set_sequence_parallel_group(group): | |
| global _sequence_parallel_group | |
| _sequence_parallel_group = group | |
| def get_sequence_parallel_group(): | |
| return _sequence_parallel_group | |
| def get_sequence_parallel_world_size(): | |
| if _sequence_parallel_group is None or not dist.is_initialized(): | |
| return 1 | |
| return dist.get_world_size(_sequence_parallel_group) | |
| def get_sequence_parallel_rank(): | |
| if _sequence_parallel_group is None or not dist.is_initialized(): | |
| return 0 | |
| return dist.get_rank(_sequence_parallel_group) | |
| def _resolve_group(group=None): | |
| return group if group is not None else _sequence_parallel_group | |
| def all_to_all_with_grad(x, scatter_dim, gather_dim, group=None): | |
| group = _resolve_group(group) | |
| if group is None or not dist.is_initialized(): | |
| return x | |
| world_size = dist.get_world_size(group) | |
| if world_size <= 1: | |
| return x | |
| scatter_size = x.size(scatter_dim) | |
| if scatter_size % world_size != 0: | |
| raise ValueError( | |
| "all_to_all_with_grad requires the scatter dimension to be " | |
| f"divisible by the sequence-parallel world size: " | |
| f"size={scatter_size}, world_size={world_size}, " | |
| f"scatter_dim={scatter_dim}, gather_dim={gather_dim}, " | |
| f"shape={tuple(x.shape)}" | |
| ) | |
| return _AllToAllWithGrad.apply(x, scatter_dim, gather_dim, group) | |
| class _AllToAllWithGrad(torch.autograd.Function): | |
| def forward(ctx, input_tensor, scatter_dim, gather_dim, group): | |
| ctx.scatter_dim = scatter_dim | |
| ctx.gather_dim = gather_dim | |
| ctx.group = group | |
| world_size = dist.get_world_size(group) | |
| inputs = [u.contiguous() for u in input_tensor.chunk(world_size, dim=scatter_dim)] | |
| outputs = [torch.empty_like(u) for u in inputs] | |
| dist.all_to_all(outputs, inputs, group=group) | |
| return torch.cat(outputs, dim=gather_dim).contiguous() | |
| def backward(ctx, grad_output): | |
| world_size = dist.get_world_size(ctx.group) | |
| inputs = [u.contiguous() for u in grad_output.chunk(world_size, dim=ctx.gather_dim)] | |
| outputs = [torch.empty_like(u) for u in inputs] | |
| dist.all_to_all(outputs, inputs, group=ctx.group) | |
| grad_input = torch.cat(outputs, dim=ctx.scatter_dim).contiguous() | |
| return grad_input, None, None, None | |
| def gather_forward_with_grad(x, dim, group=None): | |
| group = _resolve_group(group) | |
| if group is None or not dist.is_initialized(): | |
| return x | |
| world_size = dist.get_world_size(group) | |
| if world_size <= 1: | |
| return x | |
| return _AllGatherWithGrad.apply(x, dim, group) | |
| class _AllGatherWithGrad(torch.autograd.Function): | |
| def forward(ctx, input_tensor, dim, group): | |
| ctx.dim = dim | |
| ctx.group = group | |
| ctx.rank = dist.get_rank(group) | |
| ctx.world_size = dist.get_world_size(group) | |
| outputs = [torch.empty_like(input_tensor) for _ in range(ctx.world_size)] | |
| dist.all_gather(outputs, input_tensor.contiguous(), group=group) | |
| return torch.cat(outputs, dim=dim).contiguous() | |
| def backward(ctx, grad_output): | |
| chunks = [u.contiguous() for u in grad_output.chunk(ctx.world_size, dim=ctx.dim)] | |
| grad_input = chunks[ctx.rank] | |
| dist.all_reduce(grad_input, group=ctx.group) | |
| return grad_input, None, None | |