Coverage for / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / core / shard / ops / parallel_scatter.py: 100%
44 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-07-06 05:41 +0800
« prev ^ index » next coverage.py v7.13.1, created at 2026-07-06 05:41 +0800
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"""
16Distributed implementation for Scatter operator.
17"""
19from typing import Tuple
21from .parallel_ops import DistributedOp
24def _normalize_scatter_args(input_tensor, dim, index, src):
25 return (input_tensor, dim, index, src), {}
28class ScatterDistributedOp(DistributedOp):
29 """Distributed implementation for torch.scatter."""
31 def preprocess(self, args: tuple, kwargs: dict) -> tuple:
32 """
33 Preprocess arguments for Scatter operator.
35 Args:
36 args (tuple): Input arguments (input, dim, index, src).
37 kwargs (dict): Keyword arguments (unused for scatter).
39 Returns:
40 tuple: (local_args, local_kwargs, cache_values) where local_args contains
41 local tensors and cache_values contains layouts plus dim.
42 """
43 args, kwargs = _normalize_scatter_args(*args, **kwargs)
44 input_tensor, dim, index_tensor, src = args
46 input_local = input_tensor.to_local()
47 index_local = index_tensor.to_local() if hasattr(index_tensor, '_layout') else index_tensor
48 src_local = src.to_local() if hasattr(src, '_layout') else src
49 local_args = (input_local, dim, index_local, src_local)
50 local_kwargs = {}
52 cache_values = [
53 input_tensor.layout,
54 dim,
55 index_tensor.layout if hasattr(index_tensor, '_layout') else None,
56 src.layout if hasattr(src, '_layout') else None,
57 ]
58 return local_args, local_kwargs, cache_values
60 def infer_layout(self, cache_values: list) -> Tuple[tuple, None]:
61 """
62 Infer output layout for Scatter operator.
64 Rules:
65 1. Input must not have Partial status.
66 2. Input must be a DTensor with a valid layout.
67 3. dim must be an integer within the valid range [-ndim, ndim-1].
68 4. The scatter dimension must be replicated (not sharded).
69 5. Index layout must match input layout (if index is a DTensor).
70 6. Src layout must match input layout (if src is a DTensor).
71 7. Output layout is identical to input layout.
73 Args:
74 cache_values (list): [input_layout, dim, index_layout, src_layout]
75 where index_layout and src_layout may be None.
77 Returns:
78 tuple: ((output_layout,), None)
80 Raises:
81 ValueError: If any rule above is violated.
82 """
83 if not self._allow_partial_inputs:
84 self._check_partial_inputs([cache_values[0]])
86 input_layout = cache_values[0]
87 if input_layout is None:
88 raise ValueError(
89 f"For {self.op_name}, input should be a DTensor with a valid layout, "
90 f"but got None."
91 )
93 dim = cache_values[1]
94 if not isinstance(dim, int):
95 raise ValueError(
96 f"For {self.op_name}, dim should be an integer, "
97 f"but got {type(dim)}."
98 )
100 alias_map = input_layout.alias_tensor_map
101 ndim = len(alias_map)
103 if dim < 0:
104 dim += ndim
106 if dim < 0 or dim >= ndim:
107 raise ValueError(
108 f"For {self.op_name}, dim should be in range [{-ndim}, {ndim - 1}], "
109 f"but got {cache_values[1]}."
110 )
112 # Scatter dimension must be replicated
113 dim_alias = alias_map[dim]
114 if dim_alias != "None":
115 raise ValueError(
116 f"For {self.op_name}, scatter dim should be replicated, "
117 f"but dim {cache_values[1]} is mapped to {dim_alias}."
118 )
120 # Index layout must match input layout
121 index_layout = cache_values[2]
122 if index_layout is not None:
123 index_alias = index_layout.alias_tensor_map
124 if index_alias != alias_map:
125 raise ValueError(
126 f"For {self.op_name}, index layout should match input layout, "
127 f"but got {index_alias} vs {alias_map}."
128 )
130 # Src layout must match input layout
131 src_layout = cache_values[3]
132 if src_layout is not None:
133 src_alias = src_layout.alias_tensor_map
134 if src_alias != alias_map:
135 raise ValueError(
136 f"For {self.op_name}, src layout should match input layout, "
137 f"but got {src_alias} vs {alias_map}."
138 )
140 return ((input_layout,), None)