Coverage for / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / core / tensor_parallel / loss_parallel_ops_common.py: 39%
70 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 2025-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"""Common utilities for loss_parallel operations.
17This module contains platform-agnostic helper functions and validation logic
18shared between PyTorch and MindSpore implementations.
19"""
21from __future__ import annotations
23import warnings
24from typing import Any, Optional, TYPE_CHECKING
26from hyper_parallel.core.dtensor.dtensor import DTensor
27from hyper_parallel.core.dtensor.placement_types import Shard, Replicate
28from hyper_parallel.core.tensor_parallel.loss_parallel import is_loss_parallel_active
30if TYPE_CHECKING:
31 from hyper_parallel.core.dtensor.device_mesh import DeviceMesh
33__all__ = [
34 "_is_dtensor",
35 "_is_shard_on_last_dim",
36 "_is_replicate",
37 "_get_mesh_and_dim",
38 "_get_local_tensor",
39 "_get_full_tensor",
40 "_validate_target_type_base",
41 "_validate_mesh_and_shard",
42 "_validate_cross_entropy_params",
43 "_check_context_and_layout",
44]
47def _is_dtensor(obj: Any) -> bool:
48 """Check if object is a DTensor."""
49 return isinstance(obj, DTensor)
52def _is_shard_on_last_dim(dtensor: DTensor) -> bool:
53 """Check if DTensor is Shard on the last dimension."""
54 if not _is_dtensor(dtensor):
55 return False
56 placements = dtensor.placements
57 if not placements:
58 return False
59 last_placement = placements[-1]
60 if isinstance(last_placement, Shard):
61 return last_placement.dim in (-1, len(dtensor.shape) - 1)
62 return False
65def _is_replicate(dtensor: DTensor) -> bool:
66 """Check if DTensor is Replicate."""
67 if not _is_dtensor(dtensor):
68 return False
69 return all(isinstance(p, Replicate) for p in dtensor.placements)
72def _get_mesh_and_dim(dtensor: DTensor) -> tuple[Optional["DeviceMesh"], Optional[int]]:
73 """Get mesh and shard dimension from DTensor."""
74 mesh = dtensor.device_mesh
75 shard_dim = None
76 for p in dtensor.placements:
77 if isinstance(p, Shard):
78 shard_dim = p.dim if p.dim >= 0 else len(dtensor.shape) + p.dim
79 break
80 return mesh, shard_dim
83def _get_local_tensor(tensor):
84 """Get DTensor local tensor, or return regular tensor."""
85 if _is_dtensor(tensor):
86 return tensor._local_tensor # type: ignore # pylint: disable=W0212
87 return tensor
90def _get_full_tensor(tensor):
91 """Get full tensor (all_gather)."""
92 if _is_dtensor(tensor):
93 return tensor.full_tensor() # type: ignore
94 return tensor
97def _validate_target_type_base(is_floating: bool) -> None:
98 """Validate target type (base check).
100 Args:
101 is_floating: Whether target tensor has floating dtype.
103 Raises:
104 ValueError: If target is floating point (probabilistic).
105 """
106 if is_floating:
107 raise ValueError(
108 "Probabilistic target (float) is not supported in loss_parallel. "
109 "Target must be class indices (int64)."
110 )
113def _validate_mesh_and_shard(dtensor: DTensor, strict: bool = True) -> None:
114 """Validate mesh and Shard dimension.
116 Args:
117 dtensor: Input DTensor.
118 strict: Whether to raise error or warn on validation failure.
120 Raises:
121 ValueError: If mesh is not 1D or input is not Shard(-1) in strict mode.
122 """
123 mesh = dtensor.device_mesh
125 if mesh.ndim != 1:
126 if strict:
127 raise ValueError(
128 f"Expected 1D TP mesh, got {mesh.ndim}D mesh. "
129 "Slice a 1D sub-mesh first: mesh['tp']"
130 )
131 warnings.warn(
132 f"Expected 1D TP mesh, got {mesh.ndim}D mesh. "
133 "This may cause incorrect results."
134 )
136 if not _is_shard_on_last_dim(dtensor):
137 if strict:
138 raise ValueError(
139 "Expected Shard(-1) on class dimension. "
140 f"Got placements: {dtensor.placements}"
141 )
142 warnings.warn(
143 f"Expected Shard(-1) on class dimension. "
144 f"Got placements: {dtensor.placements}. "
145 "This may cause incorrect results."
146 )
149def _validate_cross_entropy_params(
150 input_tensor,
151 target,
152 weight: Optional[Any],
153 size_average: Optional[bool],
154 ignore_index: int, # pylint: disable=W0613
155 reduce: Optional[bool],
156 reduction: str,
157 label_smoothing: float,
158 is_floating_fn,
159) -> None:
160 """Validate cross_entropy parameter support.
162 Args:
163 input_tensor: Input tensor.
164 target: Target tensor.
165 weight: Optional weight tensor.
166 size_average: Deprecated parameter.
167 ignore_index: Index to ignore (reserved for future use).
168 reduce: Deprecated parameter.
169 reduction: Reduction method.
170 label_smoothing: Label smoothing factor.
171 is_floating_fn: Platform-specific function to check if tensor is floating.
173 Raises:
174 ValueError: If parameters are invalid.
175 """
176 _validate_target_type_base(is_floating_fn(target))
178 if _is_dtensor(input_tensor):
179 if not _is_shard_on_last_dim(input_tensor):
180 raise ValueError(
181 "input must be Shard(-1) on class dimension. "
182 f"Got placements: {input_tensor.placements}"
183 )
184 else:
185 raise ValueError(
186 "input must be a DTensor when using loss_parallel. "
187 f"Got type: {type(input)}"
188 )
190 if weight is not None and _is_dtensor(weight):
191 if not _is_replicate(weight):
192 raise ValueError(
193 "weight must be Replicate when it's a DTensor. "
194 f"Got placements: {weight.placements}"
195 )
197 if size_average is not None or reduce is not None:
198 warnings.warn(
199 "size_average and reduce arguments are deprecated. "
200 "Please use reduction='mean' or reduction='sum' instead.",
201 DeprecationWarning,
202 )
204 if label_smoothing != 0.0:
205 raise ValueError(
206 "label_smoothing is not supported in loss_parallel. "
207 "Please set label_smoothing=0.0 or disable loss_parallel."
208 )
210 if reduction not in ("none", "mean", "sum"):
211 raise ValueError(f"Invalid reduction: {reduction}. Must be 'none', 'mean', or 'sum'.")
214def _check_context_and_layout(dtensor: DTensor) -> None: # pylint: disable=W0613
215 """Check context and layout.
217 Args:
218 dtensor: Input DTensor (reserved for future layout validation).
220 Raises:
221 ValueError: If not in loss_parallel context.
222 """
223 if not is_loss_parallel_active():
224 raise ValueError(
225 "Shard logits detected but not in loss_parallel context. "
226 "Please wrap with loss_parallel() context manager."
227 )