Coverage for / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / platform / mindspore / dtensor.py: 45%
120 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-08-21 04:29 +0800
« prev ^ index » next coverage.py v7.13.1, created at 2026-08-21 04:29 +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"""mindspore dtensor base"""
16from mindspore._c_expression import NoFallbackGuard, _DisableMsDispatchMode
17from mindspore.common.tensor import Tensor
18from mindspore.common.initializer import initializer
21class DTensorBase(Tensor):
22 """
23 DTensorBase - Base class for distributed tensors in MindSpore.
25 This class extends Tensor to support distributed tensor operations with
26 device mesh and placement specifications.
27 """
29 def __new__(cls, local_tensor, device_mesh=None, placements=None, layout=None, shape=None):
30 """
31 Create a new DTensorBase instance.
33 Args:
34 local_tensor: The local tensor shard or another DTensorBase instance.
35 device_mesh: The device mesh describing the device topology.
36 placements: The placement strategy for each mesh dimension.
37 layout: Optional pre-built Layout. When supplied, ``__init_data__``
38 reuses it directly and skips ``_build_layout`` (hot-path fast
39 construction, see ``DTensor.from_local_with_layout``).
40 shape: Optional logical global tensor shape.
41 """
42 # Fast path: a pre-built layout is only ever supplied by the internal
43 # wrap_output / from_local_with_layout hot path, where local_tensor is a
44 # freshly produced plain op-output Tensor (never a DTensorBase) already on
45 # the compute device, and device_mesh/placements are known-valid. Skip the
46 # ABCMeta isinstance(local_tensor, DTensorBase) check (MindSpore Tensor's
47 # metaclass is ABCMeta, so that isinstance is ~10x a normal one), the three
48 # None guards, and the device-placement guard — all pure per-output overhead.
49 if layout is not None:
50 t = Tensor._make_subclass(cls, local_tensor)
51 t.__init_data__(local_tensor, device_mesh, placements, layout, shape)
52 return t
54 npu_device = "Ascend"
55 if isinstance(local_tensor, DTensorBase):
56 src = local_tensor
57 local_tensor = src.to_local()
58 device_mesh = src.device_mesh
59 placements = src._alias_placements()
60 shape = getattr(src, "_global_shape", None)
61 else:
62 if local_tensor is None:
63 raise ValueError(
64 "DTensorBase: local_tensor must not be None when constructing from a raw tensor."
65 )
66 if device_mesh is None:
67 raise ValueError(
68 "DTensorBase: device_mesh must be a DeviceMesh instance, got None."
69 )
70 if placements is None:
71 raise ValueError(
72 "DTensorBase: placements must be a sequence of Placement objects, got None."
73 )
75 if local_tensor.has_init:
76 local_tensor.init_device = npu_device
77 else:
78 dev = local_tensor.device
79 if dev != "meta" and not dev.startswith(npu_device):
80 local_tensor = local_tensor.to(npu_device)
82 t = Tensor._make_subclass(cls, local_tensor)
83 t.__init_data__(local_tensor, device_mesh, placements, layout, shape)
84 return t
86 def asnumpy(self):
87 """
88 Numpy value of local tensor.
89 """
90 return self._local_tensor.asnumpy()
92 def __str__(self):
93 return str(self._local_tensor)
95 def __copy__(self):
96 """
97 Create a shallow copy of the DTensorBase instance.
99 This method ensures that device_mesh and placements are correctly
100 propagated when creating a copy (e.g., for optimizer states).
101 """
102 # Get device_mesh and placements from layout (prefer alias_placements to preserve multi-axis ordering)
103 device_mesh = getattr(self, '_device_mesh', None)
104 placements = None
106 if hasattr(self, '_layout') and self._layout is not None:
107 if device_mesh is None:
108 device_mesh = self._layout.mesh
109 placements = self._layout.alias_placements
111 if placements is None:
112 placements = getattr(self, '_placements', None)
114 if device_mesh is None or placements is None:
115 raise ValueError(
116 "DTensorBase.__copy__: cannot copy without device_mesh and placements; "
117 f"device_mesh={device_mesh!r}, placements={placements!r}. "
118 "Ensure the tensor was constructed with a valid layout."
119 )
121 if self._local_tensor.has_init:
122 obj = DTensorBase.__new__(
123 type(self),
124 initializer(self._local_tensor.init, self._local_tensor.shape, self._local_tensor.dtype),
125 device_mesh,
126 placements,
127 shape=getattr(self, "_global_shape", None),
128 )
129 else:
130 obj = DTensorBase.__new__(
131 type(self),
132 self._local_tensor.clone(),
133 device_mesh,
134 placements,
135 shape=getattr(self, "_global_shape", None),
136 )
137 filtered_dict = {k: v for k, v in self.__dict__.items() if k != '_local_tensor'}
138 obj.__dict__.update(filtered_dict)
139 return obj
141 # pylint: disable=W0211, W0102, C0415, G.NAM.05
142 def __fallback__(self, func, args={}, kwargs=None):
143 if kwargs is None:
144 kwargs = {}
145 from hyper_parallel.core.shard._op_dispatch import _OP_DISPATCHER
146 with NoFallbackGuard():
147 out = _OP_DISPATCHER.dispatch(func, args, kwargs)
148 return out
150 # pylint: disable=W0212
151 def _need_contiguous(self):
152 """_need_contiguous"""
153 return self._local_tensor._need_contiguous()
155 @property
156 def device(self):
157 """Device info for dtensor"""
158 device_info = self._local_tensor.device
159 return device_info.split(':', 1)[0]
161 @property
162 # pylint: disable=C2801
163 def data(self):
164 """Return the underlying tensor data, preserving the DTensorBase subclass."""
165 return Tensor.data.__get__(self, type(self))
167 @data.setter
168 # pylint: disable=C2801
169 def data(self, value):
170 """Set the underlying tensor data, extracting the local shard if a DTensor is given."""
171 local_value = value.to_local() if isinstance(value, DTensorBase) else value
172 with _DisableMsDispatchMode():
173 Tensor.data.__set__(self, local_value)
174 Tensor.data.__set__(self._local_tensor, local_value)
176 # pylint: disable=W0212
177 def set_data(self, data, slice_shape=False):
178 """
179 Set shape/dtype/storage for dtensor and local tensor.
181 Args:
182 data (Tensor): New tensor payload.
183 slice_shape (bool): Kept for MindSpore `Parameter.set_data` API
184 compatibility. Static-graph slicing semantics are not used by
185 hyper_parallel, so this flag is accepted but ignored.
186 """
187 _ = slice_shape
188 if not isinstance(data, Tensor):
189 raise ValueError(f"The data type {type(data)} is not Tensor")
190 if data.has_init:
191 data.init_data()
192 data = data.to(self.device)
193 if isinstance(data, DTensorBase):
194 self._local_tensor._update_data(data.to_local())
195 self._device_mesh = data.device_mesh
196 self._placements = data.placements
197 self._layout = data.layout
198 self._global_shape = getattr(data, "_global_shape", tuple(data.shape))
199 self._update_data(self._local_tensor)
200 return
202 self._local_tensor._update_data(data)
203 self._update_data(data)
205 @property
206 def has_init(self):
207 """
208 Property to check if the initialization state is set in the local tensor.
210 Returns:
211 bool: True if the local tensor has the 'has_init' attribute, False otherwise.
212 """
213 if not hasattr(self._local_tensor, "has_init"):
214 return False
215 return self._local_tensor.has_init
217 @property
218 def init(self):
219 """
220 Property to get the initialization value from the local tensor.
222 Returns:
223 Any: The initialization value stored in the local tensor if the 'init' attribute exists;
224 None if the 'init' attribute is not present in the local tensor.
225 """
226 if not hasattr(self._local_tensor, "init"):
227 return None
228 return self._local_tensor.init
230 @init.setter
231 def init(self, init_value):
232 """
233 Setter for the initialization value, which assigns the value to the local tensor's 'init' attribute.
235 Args:
236 init_value: The value to be set as the initialization value in the local tensor.
237 """
238 self._local_tensor.init = init_value
240 @property
241 def local_param_info(self):
242 """
243 Property to get the param_info value from the local tensor.
245 Returns:
246 Any: The param_info value stored in the local tensor if the 'param_info' attribute exists;
247 None if the 'param_info' attribute is not present in the local tensor.
248 """
249 if not hasattr(self._local_tensor, "param_info"):
250 return None
251 return self._local_tensor.param_info
253 @local_param_info.setter
254 def local_param_info(self, local_param_info_value):
255 """
256 Setter for local_param_info value, which assigns the value to the local tensor's 'param_info' attribute.
258 Args:
259 local_param_info_value: The value to be set as the param_info value in the local tensor.
260 """
261 self._local_tensor.param_info = local_param_info_value
263 def _alias_placements(self):
264 """Return alias_placements from layout, falling back to _placements."""
265 if hasattr(self, '_layout') and self._layout is not None:
266 return self._layout.alias_placements
267 return self._placements
269 def to(self, *args, **kwargs):
270 """Move the DTensor to a different device or dtype.
272 This method overrides the base Tensor.to() to properly reconstruct
273 a DTensor with device_mesh and placements preserved. Uses _make_subclass
274 to avoid issues with Parameter subclasses that don't accept extra kwargs.
276 Args:
277 *args: Arguments passed to the underlying tensor's to() method.
278 **kwargs: Keyword arguments for the tensor conversion.
280 Returns:
281 DTensorBase: A new DTensor with the converted local tensor.
282 """
283 new_local = self._local_tensor.to(*args, **kwargs)
284 new_dt = Tensor._make_subclass(type(self), new_local)
285 new_dt.__init_data__(
286 new_local,
287 self._device_mesh,
288 self._alias_placements(),
289 shape=getattr(self, "_global_shape", None),
290 )
291 return new_dt