Coverage for / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / core / shard / ops / parallel_flatten.py: 89%
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 Flatten operator.
17"""
18from typing import Tuple
20from hyper_parallel.core.shard.ops.parallel_reshape import ReshapeDistributedOp
23def _normalize_flatten_args(x, start_dim=0, end_dim=-1):
24 return (x, start_dim, end_dim), {}
27class FlattenDistributedOp(ReshapeDistributedOp):
28 """Distributed implementation for torch.flatten."""
30 def preprocess(self, args: tuple, kwargs: dict) -> tuple:
31 """
32 Preprocess arguments for Flatten operator.
34 Args:
35 args (tuple): Input tensor followed by optional start_dim and end_dim.
36 kwargs (dict): Optional keyword arguments.
38 Returns:
39 tuple: (local_args, local_kwargs, cache_values)
40 """
41 args, _ = _normalize_flatten_args(*args, **kwargs)
42 input_tensor, start_dim, end_dim = args[0], args[1], args[2]
43 local_args = (input_tensor.to_local(), start_dim, end_dim)
44 local_kwargs = {}
45 cache_values = [input_tensor.layout, start_dim, end_dim, tuple(input_tensor.shape)]
46 return local_args, local_kwargs, cache_values
48 def infer_layout(self, cache_values: list) -> Tuple[tuple, None]:
49 """
50 Infer output layout for Flatten operator.
52 Rules:
53 1. Partial input is allowed and preserved by reshape layout inference.
54 2. input_shape must be provided and match the input rank.
55 3. start_dim and end_dim must be integers within the valid input rank.
56 4. If start_dim >= end_dim after normalization, the output layout is the
57 input layout.
58 5. Otherwise, dimensions from start_dim through end_dim are merged using
59 reshape-compatible sharding rules.
61 Args:
62 cache_values (list): [input_layout, start_dim, end_dim, input_shape].
64 Returns:
65 tuple: ((output_layout,), None)
67 Raises:
68 ValueError: If cache_values are invalid, dimensions are out of range,
69 or the flatten would change sharded slices incompatibly.
70 """
71 input_layout, start_dim, end_dim, input_shape = (
72 cache_values[0], cache_values[1], cache_values[2], cache_values[3]
73 )
74 if input_layout is None:
75 raise ValueError(
76 f"For {self.op_name}, flatten requires a valid input tensor layout."
77 )
78 if not isinstance(input_shape, (list, tuple)):
79 raise ValueError(
80 f"For {self.op_name}, input_shape should be list or tuple, "
81 f"but got {type(input_shape)}."
82 )
83 if len(input_shape) != len(input_layout.tensor_map):
84 raise ValueError(
85 f"For {self.op_name}, input shape rank should match layout rank, "
86 f"but got {len(input_shape)} and {len(input_layout.tensor_map)}."
87 )
88 if not isinstance(start_dim, int) or not isinstance(end_dim, int):
89 raise ValueError(
90 f"For {self.op_name}, start_dim and end_dim should be int, "
91 f"but got {type(start_dim)} and {type(end_dim)}."
92 )
94 ndim = len(input_shape)
96 if ndim == 0:
97 out_layout = input_layout.__class__.from_device_mesh(input_layout.mesh)
98 out_layout.set_placements(input_layout.placements)
99 out_layout.placement_to_tensor_map(1)
100 return ((out_layout,), None)
102 if start_dim < 0:
103 start_dim += ndim
104 if end_dim < 0:
105 end_dim += ndim
107 start_dim_invalid = start_dim < 0 or start_dim >= ndim
108 end_dim_invalid = end_dim < 0 or end_dim >= ndim
109 if start_dim_invalid or end_dim_invalid:
110 raise ValueError(
111 f"For {self.op_name}, dimension out of range "
112 f"(start_dim={start_dim}, end_dim={end_dim}, ndim={ndim})."
113 )
115 if start_dim >= end_dim:
116 return ((input_layout,), None)
118 flattened_size = 1
119 for i in range(start_dim, end_dim + 1):
120 flattened_size *= input_shape[i]
121 dst_shape = list(input_shape[:start_dim]) + [flattened_size] + list(input_shape[end_dim + 1:])
123 out_layout, _ = self._infer_reshape_layout(input_layout, dst_shape, input_shape)
124 return ((out_layout,), None)