Coverage for  / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / core / dtensor / dtensor.py: 88%

439 statements  

« 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"""dtensor""" 

16import copy as cp 

17import inspect 

18import logging 

19import warnings 

20from typing import Any, Callable, Optional, Sequence, Set, Tuple, Union 

21 

22import numpy as np 

23 

24from hyper_parallel.core.dtensor._collective_utils import mesh_broadcast, mesh_scatter 

25from hyper_parallel.core.dtensor._ragged_utils import ( 

26 _compute_ragged_slice, 

27 _layout_has_ragged_shard, 

28 _normalize_global_shape, 

29 _scatter_ragged_tensor, 

30 _slice_ragged_tensor, 

31) 

32from hyper_parallel.core.dtensor.device_mesh import _mesh_resources 

33from hyper_parallel.core.dtensor.layout import ( 

34 DeviceMesh, 

35 Layout, 

36 _get_slice_tensor_by_layout, 

37) 

38from hyper_parallel.core.dtensor.placement_types import Partial, Placement, Replicate, StridedShard 

39from hyper_parallel.platform import get_platform 

40from hyper_parallel.platform.platform import PlatformType 

41from hyper_parallel.core.utils import compute_local_shape_and_global_offset 

42 

43platform = get_platform() 

44DTensorBase = platform.DTensorBase 

45Tensor = platform.Tensor 

46 

47logger = logging.getLogger(__name__) 

48 

49 

50def _device_meshes_are_compatible(lhs: Any, rhs: Any) -> bool: 

51 """Return whether two mesh objects describe the same device topology.""" 

52 if lhs is rhs: 

53 return True 

54 if not isinstance(lhs, DeviceMesh) or not isinstance(rhs, DeviceMesh): 

55 return False 

56 return lhs.device_type == rhs.device_type and lhs.to_hash() == rhs.to_hash() 

57 

58 

59class SkipDTensorDispatch(): 

60 """Context manager that disables DTensor op dispatch for the enclosed block. 

61 

62 Args: 

63 no_skip: Optional set of op callables or canonical op name strings that 

64 should still be dispatched through DTensor even within this context. 

65 All other ops bypass DTensor dispatch and operate on local tensors. 

66 

67 Example: 

68 >>> import torch 

69 >>> with SkipDTensorDispatch(no_skip={torch.zeros_like}): 

70 ... # zeros_like still goes through DTensor dispatch; 

71 ... # everything else uses the local tensor path. 

72 ... result = torch.zeros_like(dtensor) 

73 """ 

74 

75 def __init__(self, no_skip: Optional[Set] = None): 

76 self._no_skip_names: frozenset = frozenset() 

77 if no_skip: 

78 names = set() 

79 for op in no_skip: 

80 if isinstance(op, str): 

81 names.add(op) 

82 else: 

83 names.add(platform.get_op_name(op)) 

84 self._no_skip_names = frozenset(names) 

85 self._dispatch_token = None 

86 self._ops_token = None 

87 

88 def __enter__(self): 

89 # pylint: disable=C0415 

90 from hyper_parallel.core.shard._op_dispatch import _dtensor_dispatch_disabled, _no_skip_ops 

91 self._dispatch_token = _dtensor_dispatch_disabled.set(True) 

92 if self._no_skip_names: 

93 self._ops_token = _no_skip_ops.set(_no_skip_ops.get() | self._no_skip_names) 

94 

95 def __exit__(self, exc_type, exc_val, exc_tb): 

96 # pylint: disable=C0415 

97 from hyper_parallel.core.shard._op_dispatch import _dtensor_dispatch_disabled, _no_skip_ops 

98 if self._ops_token is not None: 

99 _no_skip_ops.reset(self._ops_token) 

100 self._ops_token = None 

101 _dtensor_dispatch_disabled.reset(self._dispatch_token) 

102 self._dispatch_token = None 

103 

104 

105# Cache for _build_layout to avoid redundant Layout computations 

106# Key: (device_mesh.to_hash(), tuple(placements), tensor_dim) 

107# Value: Layout 

108_LAYOUT_CACHE = {} 

109 

110 

111def _is_alias_placements(placements) -> bool: 

112 """ 

113 Check if placements use alias strings rather than Placement objects. 

114 

115 Alias placements use mesh dimension names (strings) to specify 

116 the sharding strategy, e.g., ("dp", "tp") or (("dp", "tp"), "None"). 

117 All elements must be strings or tuples of strings for the sequence 

118 to be recognized as alias-style. 

119 

120 Args: 

121 placements: A sequence of placement specifications. 

122 

123 Returns: 

124 bool: True if all elements are alias strings or tuples of strings. 

125 """ 

126 if len(placements) == 0: 

127 return False 

128 for p in placements: 

129 if isinstance(p, str): 

130 continue 

131 if isinstance(p, tuple) and len(p) > 0 and all(isinstance(x, str) for x in p): 

132 continue 

133 return False 

134 return True 

135 

136 

137def _build_layout( 

138 device_mesh: DeviceMesh, 

139 placements: Union[Sequence[Placement], Sequence[Union[str, Tuple[str, ...]]]], 

140 tensor_dim: int 

141) -> Layout: 

142 """ 

143 Build Layout from device_mesh and placements. 

144 

145 This function uses a cache to avoid redundant Layout computations 

146 for the same (device_mesh, placements, tensor_dim) combination. 

147 

148 Args: 

149 device_mesh: The device mesh describing the device topology. 

150 placements: Supports two styles: 

151 - Placement objects (Shard, Replicate, etc.) 

152 - Alias strings ("dp", "None", ("dp", "tp"), etc.), length must 

153 equal the number of tensor dimensions (``tensor_dim``). 

154 tensor_dim: Number of dimensions in the tensor. 

155 

156 Returns: 

157 Layout: The built layout object. 

158 

159 Raises: 

160 ValueError: If alias placements length does not match tensor dimensions. 

161 """ 

162 mesh_key = device_mesh.to_hash() 

163 placements_key = tuple(placements) 

164 cache_key = (mesh_key, placements_key, tensor_dim) 

165 

166 if cache_key in _LAYOUT_CACHE: 

167 return _LAYOUT_CACHE[cache_key] 

168 

169 layout = Layout.from_device_mesh(device_mesh) 

170 

171 if _is_alias_placements(placements): 

172 if len(placements) != tensor_dim: 

173 raise ValueError( 

174 f"Alias placements length ({len(placements)}) must equal " 

175 f"tensor dimensions ({tensor_dim})." 

176 ) 

177 result = layout(*placements) 

178 else: 

179 result = layout(placements) 

180 result.placement_to_tensor_map(tensor_dim) 

181 

182 _LAYOUT_CACHE[cache_key] = result 

183 

184 return result 

185 

186 

187def _is_broadcastable(src_shape: Sequence[int], dst_shape: Sequence[int]) -> bool: 

188 """Return True iff ``src_shape`` is broadcastable to ``dst_shape``. 

189 

190 Standard NumPy / PyTorch right-aligned broadcast rule: ``src`` cannot 

191 have more dimensions than ``dst``; each right-aligned dimension pair 

192 must be equal, or ``src``'s dimension must be 1. 

193 """ 

194 src_shape = tuple(src_shape) 

195 dst_shape = tuple(dst_shape) 

196 if len(src_shape) > len(dst_shape): 

197 return False 

198 for i in range(1, len(src_shape) + 1): 

199 s, d = src_shape[-i], dst_shape[-i] 

200 if s not in (d, 1): 

201 return False 

202 return True 

203 

204 

205def _device_spec(device: Any) -> Tuple[str, Optional[int]]: 

206 """Return normalised ``(device_type, device_index)``. 

207 

208 Handles device objects and strings such as ``"npu:0"``. 

209 """ 

210 device_type = getattr(device, "type", None) 

211 # Only read .index from objects that also have a .type attribute 

212 # (i.e. torch.device). Avoids capturing str.index on plain strings. 

213 device_index = ( 

214 getattr(device, "index", None) if device_type is not None else None 

215 ) 

216 device_text = str(device).lower() 

217 

218 if device_type is None: 

219 parts = device_text.split(":", maxsplit=1) 

220 device_type = parts[0] 

221 if len(parts) == 2 and parts[1].isdigit(): 

222 device_index = int(parts[1]) 

223 

224 return str(device_type).lower(), device_index 

225 

226 

227class DTensor(DTensorBase): 

228 """ 

229 DTensor - Distributed Tensor 

230 

231 A DTensor represents a tensor that is distributed across multiple devices 

232 according to a DeviceMesh and placement specifications. 

233 

234 Args: 

235 local_tensor (Tensor): The local tensor shard on this device. 

236 device_mesh (DeviceMesh): The device mesh describing the device topology. 

237 placements: The placement strategy. Supports two styles: 

238 - Placement objects (e.g., ``[Shard(0), Replicate()]``). 

239 - Alias strings (e.g., ``("dp", "None")`` or 

240 ``(("dp", "tp"), "None")``), length must equal the number of 

241 tensor dimensions. 

242 

243 Example: 

244 >>> mesh = init_device_mesh(device_type="npu", mesh_shape=(2, 2), mesh_dim_names=("dp", "tp")) 

245 >>> local_tensor = Tensor(np.ones((4, 4))) 

246 >>> # Placement style 

247 >>> dtensor = DTensor.from_local(local_tensor, mesh, [Shard(0), Replicate()]) 

248 >>> # Alias style — length matches tensor dims 

249 >>> dtensor = DTensor.from_local(local_tensor, mesh, ("dp", "None")) 

250 """ 

251 _local_tensor: Tensor 

252 _device_mesh: DeviceMesh 

253 _placements: Sequence[Placement] 

254 

255 def __init_data__( 

256 self, 

257 local_tensor: Tensor, 

258 device_mesh: DeviceMesh, 

259 placements: Union[Sequence[Placement], Sequence[Union[str, Tuple[str, ...]]]], 

260 layout: Optional[Layout] = None, 

261 shape: Optional[Tuple[int, ...]] = None, 

262 ): 

263 self._local_tensor = local_tensor 

264 self._device_mesh = device_mesh 

265 tensor_dim = len(shape) if shape is not None else len(local_tensor.shape) 

266 # Fast path: when an already-built Layout is supplied (e.g. output layouts 

267 # cached by infer_layout and passed straight through wrap_output), reuse it 

268 # directly and skip _build_layout (which otherwise recomputes device_mesh.to_hash(), 

269 # tuple(placements) and a cache lookup on every single output construction). 

270 self._layout = layout if layout is not None else _build_layout( 

271 device_mesh, placements, tensor_dim 

272 ) 

273 self._placements = tuple(self._layout.placements) 

274 is_ragged = _layout_has_ragged_shard(self._layout) 

275 if is_ragged and shape is None: 

276 raise ValueError( 

277 "DTensor.from_local with RaggedShard requires an explicit global shape" 

278 ) 

279 if shape is not None: 

280 self._global_shape = _normalize_global_shape(shape) 

281 else: 

282 self._global_shape = tuple(self._layout.get_global_shape(local_tensor.shape)) 

283 if ( 

284 shape is not None 

285 and ( 

286 self._layout.tensor_map is None 

287 or len(self._layout.tensor_map) != len(self._global_shape) 

288 ) 

289 ): 

290 raise ValueError( 

291 "DTensor global shape rank must match layout tensor_map rank, " 

292 f"got global_shape={self._global_shape!r}, tensor_map={self._layout.tensor_map!r}" 

293 ) 

294 if is_ragged: 

295 if hasattr(local_tensor, "is_contiguous") and not local_tensor.is_contiguous(): 

296 raise ValueError("RaggedShard local tensor must be contiguous") 

297 if len(local_tensor.shape) != 1: 

298 raise ValueError( 

299 "RaggedShard local tensor must use one-dimensional flat storage, " 

300 f"got local_shape={tuple(local_tensor.shape)!r}" 

301 ) 

302 expected = _compute_ragged_slice(self._global_shape, self._layout) 

303 if local_tensor.numel() != expected.local_numel: 

304 raise ValueError( 

305 "RaggedShard local tensor numel does not match its allocation, " 

306 f"got actual={local_tensor.numel()}, expected={expected.local_numel}, " 

307 f"global_shape={self._global_shape!r}, placement={self._layout.ragged_shard.placement!r}" 

308 ) 

309 

310 @property 

311 def device_mesh(self) -> DeviceMesh: 

312 """The device mesh of this DTensor.""" 

313 return self._device_mesh 

314 

315 @property 

316 def placements(self) -> Sequence[Placement]: 

317 """The placements of this DTensor.""" 

318 return self._placements 

319 

320 @property 

321 def layout(self) -> Layout: 

322 """Internal layout for redistribution (for backward compatibility).""" 

323 if not hasattr(self, '_layout'): 

324 return None 

325 return self._layout 

326 

327 @staticmethod 

328 def from_local( 

329 local_tensor: Tensor, 

330 device_mesh: DeviceMesh, 

331 placements: Union[Sequence[Placement], Sequence[Union[str, Tuple[str, ...]]]], 

332 *, 

333 run_check: bool = False, 

334 shape: Optional[Tuple[int, ...]] = None, 

335 ) -> 'DTensor': 

336 """ 

337 Create a DTensor from a local tensor with device mesh and placements. 

338 

339 Args: 

340 local_tensor (Tensor): The local tensor shard on this device. 

341 device_mesh (DeviceMesh): The device mesh describing the device topology. 

342 placements: The placement strategy. Supports two styles: 

343 - Placement objects (e.g., ``[Shard(0), Replicate()]``). 

344 - Alias strings (e.g., ``("dp", "None")`` or 

345 ``(("dp", "tp"), "None")``), length must equal the number 

346 of tensor dimensions. 

347 run_check (bool, optional): When ``True``, perform cross-rank metadata 

348 checks and broadcast replicate placements from the mesh source rank. 

349 Default: ``False``. 

350 shape (tuple[int, ...], optional): Explicit logical global shape. 

351 Required for RaggedShard. 

352 

353 Returns: 

354 DTensor: A new DTensor instance. 

355 

356 Example: 

357 >>> mesh = init_device_mesh(device_type="npu", mesh_shape=(2, 2), mesh_dim_names=("dp", "tp")) 

358 >>> local_tensor = Tensor(np.ones((4, 4))) 

359 >>> dtensor = DTensor.from_local(local_tensor, mesh, [Shard(0), Replicate()]) 

360 >>> dtensor = DTensor.from_local(local_tensor, mesh, ("dp", "None")) 

361 """ 

362 if run_check: 

363 # pylint: disable=C0415 

364 from hyper_parallel.core.dtensor._from_local_utils import run_from_local_checks 

365 tensor_dim = len(shape) if shape is not None else len(local_tensor.shape) 

366 layout = _build_layout(device_mesh, placements, tensor_dim) 

367 run_from_local_checks( 

368 local_tensor, 

369 device_mesh, 

370 layout.placements, 

371 shape=shape, 

372 ) 

373 return DTensor(local_tensor, device_mesh, placements, shape=shape) 

374 

375 @staticmethod 

376 def from_local_with_layout( 

377 local_tensor: Tensor, 

378 layout: Layout, 

379 *, 

380 shape: Optional[Tuple[int, ...]] = None, 

381 ) -> 'DTensor': 

382 """Fast DTensor construction from a local tensor and a pre-built Layout. 

383 

384 Unlike :meth:`from_local`, this does NOT rebuild the layout via 

385 ``_build_layout`` — it hands the already-built ``layout`` straight to 

386 ``__init_data__``. Intended for hot paths (e.g. ``wrap_output``) where the 

387 output Layout was already inferred and cached by ``infer_layout``, so 

388 recomputing ``device_mesh.to_hash()`` / ``tuple(placements)`` / the layout 

389 cache lookup on every output is pure waste. 

390 

391 ``layout.placements`` (a plain attribute) is passed only to satisfy the 

392 constructor's non-None check; ``__init_data__`` ignores it when ``layout`` 

393 is supplied. 

394 """ 

395 return DTensor( 

396 local_tensor, 

397 layout.mesh, 

398 layout.placements, 

399 layout, 

400 shape=shape, 

401 ) 

402 

403 def _alias_placements(self) -> Sequence[Placement]: 

404 """Return alias_placements from layout, falling back to _placements.""" 

405 if hasattr(self, '_layout') and self._layout: 

406 return self._layout.alias_placements 

407 return self._placements 

408 

409 def _from_converted_local(self, local_tensor: Tensor) -> 'DTensor': 

410 """Rebuild converted DTensor data without preserving Parameter identity.""" 

411 cls = DTensor if isinstance(self, platform.Parameter) else self.__class__ 

412 kwargs = { 

413 "device_mesh": self._device_mesh, 

414 "placements": self._alias_placements(), 

415 } 

416 if hasattr(self, "_global_shape"): 

417 kwargs["shape"] = self._global_shape 

418 return cls(local_tensor, **kwargs) 

419 

420 def to(self, *args, **kwargs): 

421 """Move the DTensor to a different device or dtype. 

422 

423 Delegates to the underlying local tensor's ``to`` method and 

424 reconstructs a DTensor preserving device_mesh and placements. 

425 

426 Args: 

427 *args (tuple): Arguments passed to the underlying tensor's ``to`` 

428 method (e.g., device or dtype). 

429 **kwargs (dict): Keyword arguments for the tensor conversion 

430 (e.g., dtype, device, non_blocking). 

431 

432 Returns: 

433 DTensor: A new DTensor with the converted local tensor. 

434 """ 

435 new_local = self._local_tensor.to(*args, **kwargs) 

436 return self._from_converted_local(new_local) 

437 

438 def float(self): 

439 """Convert the DTensor to float dtype. 

440 

441 Returns: 

442 DTensor: A new DTensor with float32 local tensor. 

443 """ 

444 new_local = self._local_tensor.float() 

445 return self._from_converted_local(new_local) 

446 

447 def type_as(self, other: Tensor) -> "DTensor": 

448 """Cast this DTensor to the dtype of ``other``. 

449 

450 This is a **local** operation — no communication. Each shard 

451 independently casts its elements to the target dtype. 

452 

453 Only the **dtype** of ``other`` is read; its shape, values, and 

454 layout are ignored. The returned DTensor preserves the 

455 device-mesh and placements of ``self`` unchanged. 

456 

457 Args: 

458 other (Tensor): A tensor whose ``.dtype`` will be used as the 

459 target type. May be a plain :class:`Tensor` or a 

460 :class:`DTensor`. Must reside on the same device as 

461 ``self``. 

462 

463 Returns: 

464 DTensor: A new DTensor with the converted local tensor. When 

465 ``self.dtype == other.dtype`` the method returns ``self`` 

466 unchanged (no-op). 

467 

468 Raises: 

469 ValueError: If ``other`` is not a Tensor. 

470 ValueError: If ``self`` has Partial placement (cast does not 

471 commute with reduction). 

472 ValueError: If ``self`` and ``other`` are on different devices. 

473 

474 Note: 

475 This implementation intentionally covers **dtype-only** 

476 conversion. PyTorch's native ``type_as`` may also handle 

477 cross-device transfers, but a DTensor cannot silently change 

478 its backend device while retaining the old ``DeviceMesh``. 

479 Use :meth:`to` for explicit device + dtype conversion. 

480 

481 Example: 

482 >>> # x is a DTensor of float16, y is a plain float32 Tensor 

483 >>> # on the same device. 

484 >>> z = x.type_as(y) 

485 >>> z.dtype == y.dtype 

486 True 

487 """ 

488 if not isinstance(other, Tensor): 

489 raise ValueError( 

490 f"type_as() argument must be a Tensor, but got " 

491 f"{type(other).__name__}." 

492 ) 

493 if hasattr(self, '_layout') and self._layout is not None: 

494 if self._layout.is_partial(): 

495 raise ValueError( 

496 "DTensor.type_as does not support Partial input; " 

497 "call reduce_partial() first." 

498 ) 

499 

500 other_local = other.to_local() if isinstance(other, DTensor) else other 

501 if self._local_tensor.device != other_local.device: 

502 raise ValueError( 

503 "DTensor.type_as requires self and other to be on the " 

504 "same device. Use to() for explicit device + dtype " 

505 "conversion." 

506 ) 

507 

508 target_dtype = other.dtype 

509 if self.dtype == target_dtype: 

510 return self 

511 new_local = self._local_tensor.to(dtype=target_dtype) 

512 return self._from_converted_local(new_local) 

513 

514 def _validate_factory_device(self, device: Any) -> None: 

515 """Raise :class:`ValueError` if ``device`` does not match the DTensor's device.""" 

516 requested_type, requested_index = _device_spec(device) 

517 local_type, local_index = _device_spec(self._local_tensor.device) 

518 if ( 

519 requested_type != local_type 

520 or ( 

521 requested_index is not None 

522 and requested_index != local_index 

523 ) 

524 ): 

525 raise ValueError( 

526 f"DTensor requires device to match the input DTensor " 

527 f"device {self._local_tensor.device}, but got {device}." 

528 ) 

529 

530 def _new_const_tensor_op( 

531 self, 

532 method_name: str, 

533 size: Union[int, Sequence[int]], 

534 *, 

535 dtype: Optional[Any] = None, 

536 device: Optional[Any] = None, 

537 requires_grad: bool = False, 

538 layout: Optional[Any] = None, 

539 pin_memory: bool = False, 

540 ) -> 'DTensor': 

541 """Create an all-``Replicate`` constant DTensor. 

542 

543 Shared implementation for ``new_zeros`` and ``new_ones``. 

544 

545 ``self`` is only used as a dtype/device reference and mesh source; 

546 its values are ignored. The output is always **all-Replicate** 

547 because every device produces identical data independently. 

548 

549 Args: 

550 method_name: 

551 ``"new_zeros"`` or ``"new_ones"`` — the local tensor 

552 factory method to call. 

553 size: 

554 Output shape — an int or a sequence of ints. 

555 dtype: 

556 Desired dtype. Defaults to ``self.dtype`` on Torch. 

557 device: 

558 Must match ``self``'s device (Torch only). 

559 requires_grad: 

560 Forwarded on Torch; rejected on MindSpore. 

561 layout: 

562 Forwarded on Torch; rejected on MindSpore. 

563 pin_memory: 

564 Forwarded on Torch; rejected on MindSpore. 

565 

566 Returns: 

567 DTensor: A new DTensor with all-``Replicate`` placements on 

568 ``self``'s ``DeviceMesh``. 

569 

570 Raises: 

571 ValueError: If a Torch-only kwarg is used on MindSpore, or 

572 ``device`` does not match the DTensor's device. 

573 """ 

574 if isinstance(size, int): 

575 size = (size,) 

576 

577 if platform.platform_type == PlatformType.MINDSPORE: 

578 if device is not None or layout is not None or requires_grad or pin_memory: 

579 raise ValueError( 

580 f"DTensor.{method_name} only supports size and dtype " 

581 "on MindSpore." 

582 ) 

583 local_kwargs = {} 

584 if dtype is not None: 

585 local_kwargs["dtype"] = dtype 

586 else: 

587 local_kwargs = {} 

588 if dtype is not None: 

589 local_kwargs["dtype"] = dtype 

590 if device is not None: 

591 self._validate_factory_device(device) 

592 # An unindexed device such as "cuda" resolves to the framework's 

593 # current device, which may differ from this DTensor's local device. 

594 local_kwargs["device"] = self._local_tensor.device 

595 if requires_grad: 

596 local_kwargs["requires_grad"] = True 

597 if layout is not None: 

598 local_kwargs["layout"] = layout 

599 if pin_memory: 

600 local_kwargs["pin_memory"] = True 

601 

602 factory = getattr(self._local_tensor, method_name) 

603 local_result = factory(size, **local_kwargs) 

604 

605 replicated_placements = [Replicate()] * self._device_mesh.ndim 

606 return DTensor.from_local( 

607 local_result, self._device_mesh, replicated_placements, 

608 ) 

609 

610 def new_zeros( 

611 self, 

612 size: Union[int, Sequence[int]], 

613 *, 

614 dtype: Optional[Any] = None, 

615 device: Optional[Any] = None, 

616 requires_grad: bool = False, 

617 layout: Optional[Any] = None, 

618 pin_memory: bool = False, 

619 ) -> 'DTensor': 

620 """Create an all-Replicate DTensor filled with zeros. 

621 

622 The output is always **fully replicated** across every device in 

623 ``self``'s ``DeviceMesh``, regardless of how ``self`` is sharded. 

624 

625 Args: 

626 size: 

627 Output shape — an int or a sequence of ints. 

628 dtype: 

629 Desired dtype. Defaults to ``self.dtype`` (Torch). 

630 Not forwarded to MindSpore unless explicitly set. 

631 device: 

632 Must match ``self``'s device. Not supported on MindSpore. 

633 requires_grad: 

634 Forwarded on Torch; rejected on MindSpore. 

635 layout: 

636 Forwarded on Torch; rejected on MindSpore. 

637 pin_memory: 

638 Forwarded on Torch; rejected on MindSpore. 

639 

640 Returns: 

641 DTensor: A new all-Replicate DTensor filled with zeros. 

642 """ 

643 return self._new_const_tensor_op( 

644 "new_zeros", size, 

645 dtype=dtype, 

646 device=device, 

647 requires_grad=requires_grad, 

648 layout=layout, 

649 pin_memory=pin_memory, 

650 ) 

651 

652 def new_ones( 

653 self, 

654 size: Union[int, Sequence[int]], 

655 *, 

656 dtype: Optional[Any] = None, 

657 device: Optional[Any] = None, 

658 requires_grad: bool = False, 

659 layout: Optional[Any] = None, 

660 pin_memory: bool = False, 

661 ) -> 'DTensor': 

662 """Create an all-Replicate DTensor filled with ones. 

663 

664 The output is always **fully replicated** across every device in 

665 ``self``'s ``DeviceMesh``, regardless of how ``self`` is sharded. 

666 

667 Args: 

668 size: 

669 Output shape — an int or a sequence of ints. 

670 dtype: 

671 Desired dtype. Defaults to ``self.dtype`` (Torch). 

672 Not forwarded to MindSpore unless explicitly set. 

673 device: 

674 Must match ``self``'s device. Not supported on MindSpore. 

675 requires_grad: 

676 Forwarded on Torch; rejected on MindSpore. 

677 layout: 

678 Forwarded on Torch; rejected on MindSpore. 

679 pin_memory: 

680 Forwarded on Torch; rejected on MindSpore. 

681 

682 Returns: 

683 DTensor: A new all-Replicate DTensor filled with ones. 

684 """ 

685 return self._new_const_tensor_op( 

686 "new_ones", size, 

687 dtype=dtype, 

688 device=device, 

689 requires_grad=requires_grad, 

690 layout=layout, 

691 pin_memory=pin_memory, 

692 ) 

693 

694 def to_local(self) -> Tensor: 

695 """ 

696 Convert DTensor to local tensor. 

697 

698 Returns: 

699 Tensor: The local tensor shard on this device. 

700 """ 

701 return self._local_tensor 

702 

703 def tolist(self): 

704 """ 

705 Convert the DTensor to a nested Python list or number. 

706 

707 This operation gathers the complete tensor on every participating rank 

708 before converting it to Python values. It is an **implicit collective**: 

709 all ranks in the DeviceMesh must participate. 

710 

711 Returns: 

712 Union[list, int, float, bool]: A nested Python list, or a Python 

713 number for a scalar DTensor. 

714 

715 Note: 

716 This triggers ``full_tensor()`` under the hood, which performs 

717 all-gather communication. For large tensors, prefer slicing or 

718 index-based access to avoid materialising the full tensor. 

719 

720 If you only need the **local shard** as a list, use 

721 ``dtensor.to_local().tolist()`` instead — that path has zero 

722 communication overhead. 

723 

724 Example: 

725 >>> mesh = init_device_mesh("npu", (2,), ("dp",)) 

726 >>> x = distribute_tensor(torch.arange(8).reshape(4, 2), mesh, [Shard(0)]) 

727 >>> x.tolist() # full data: [[0,1],[2,3],[4,5],[6,7]] 

728 >>> x.to_local().tolist() # local shard only (no comm) 

729 """ 

730 return self.full_tensor().tolist() 

731 

732 def copy_(self, src: "DTensor", non_blocking: bool = False) -> "DTensor": 

733 """In-place copy of ``src`` into this DTensor's local shard. 

734 

735 Delegates to ``Tensor.copy_`` on the underlying local tensors. 

736 Follows standard ``Tensor.copy_`` semantics: version counter is 

737 bumped and autograd edges are created when grad is enabled. 

738 

739 Constraints on ``src``: 

740 * must be a ``DTensor`` on the same or an equivalent ``DeviceMesh`` topology as ``self``; 

741 * its placements must equal ``self.placements``, OR 

742 ``src._local_tensor.numel() == 1`` (single-element broadcast); 

743 * its local shape must equal or be broadcastable to 

744 ``self._local_tensor.shape``. 

745 

746 No redistribute / implicit slicing is performed; src dtype is cast 

747 to self dtype in-place. 

748 

749 Args: 

750 src (DTensor): Source DTensor satisfying the constraints above. 

751 non_blocking (bool): Forwarded to the underlying ``copy_``. 

752 

753 Returns: 

754 DTensor: ``self``. 

755 

756 Raises: 

757 TypeError: if ``src`` is not a ``DTensor``. 

758 ValueError: if mesh, placement, or shape constraint is violated. 

759 """ 

760 if not isinstance(src, DTensor): 

761 raise TypeError( 

762 f"For DTensor.copy_, src should be a DTensor, but got {type(src).__name__}." 

763 ) 

764 src_local = src.to_local() 

765 if not _device_meshes_are_compatible(src.device_mesh, self._device_mesh): 

766 raise ValueError( 

767 f"For DTensor.copy_, src and self should share the same DeviceMesh, " 

768 f"but got src.device_mesh={src.device_mesh!r}, " 

769 f"self._device_mesh={self._device_mesh!r}." 

770 ) 

771 

772 placement_eq = tuple(src.placements) == tuple(self._placements) 

773 shape_eq = src_local.shape == self._local_tensor.shape 

774 src_is_scalar = src_local.numel() == 1 

775 

776 if not placement_eq and not src_is_scalar: 

777 raise ValueError( 

778 f"For DTensor.copy_, src.placements should equal self.placements " 

779 f"or src.numel() should be 1, but got " 

780 f"src.placements={src.placements}, " 

781 f"self.placements={self._placements}, " 

782 f"src.numel()={src_local.numel()}." 

783 ) 

784 if not shape_eq and not src_is_scalar and not _is_broadcastable( 

785 src_local.shape, self._local_tensor.shape 

786 ): 

787 raise ValueError( 

788 f"For DTensor.copy_, src local shape should be broadcastable to " 

789 f"self local shape, but got " 

790 f"src.shape={tuple(src_local.shape)}, " 

791 f"self.shape={tuple(self._local_tensor.shape)}." 

792 ) 

793 

794 self._local_tensor.copy_(src_local, non_blocking=non_blocking) 

795 return self 

796 

797 def zero_(self) -> "DTensor": 

798 """In-place fill with zeros. Returns ``self``.""" 

799 self._local_tensor.zero_() 

800 return self 

801 

802 def fill_(self, value) -> "DTensor": 

803 """In-place fill with ``value``. Returns ``self``.""" 

804 self._local_tensor.fill_(value) 

805 return self 

806 

807 @property 

808 def shape(self) -> Tuple[int, ...]: 

809 """ 

810 The global shape of this DTensor. 

811 

812 Returns: 

813 Tuple[int, ...]: The global tensor shape. 

814 """ 

815 return self._global_shape 

816 

817 def size(self, dim=None): 

818 """Return the global shape, consistent with .shape. 

819 

820 Without ``dim`` returns a tuple matching ``self.shape``. 

821 With ``dim`` returns the size of that dimension. 

822 """ 

823 global_shape = self.shape 

824 if dim is not None: 

825 return global_shape[dim] 

826 return global_shape 

827 

828 def numel(self) -> int: 

829 """Return the number of elements in this DTensor.""" 

830 return int(np.prod(self.shape)) 

831 

832 @property 

833 def ndim(self) -> int: 

834 """Return the logical global tensor rank.""" 

835 return len(self._global_shape) 

836 

837 def dim(self) -> int: 

838 """Return the logical global tensor rank.""" 

839 return len(self._global_shape) 

840 

841 @property 

842 def local_shape(self) -> Tuple[int, ...]: 

843 """ 

844 The local shape of this DTensor on this device. 

845 

846 Returns: 

847 Tuple[int, ...]: The local tensor shape. 

848 """ 

849 return self._local_tensor.shape 

850 

851 def redistribute( 

852 self, 

853 device_mesh: DeviceMesh, 

854 placements: Union[Sequence[Placement], Sequence[Union[str, Tuple[str, ...]]]] 

855 ) -> 'DTensor': 

856 """ 

857 Redistribute this DTensor to a new device mesh and placements. 

858 

859 Args: 

860 device_mesh (DeviceMesh): The target device mesh. 

861 placements: The target placements. Supports Placement objects 

862 or alias strings. 

863 

864 Returns: 

865 DTensor: A new DTensor with the specified distribution. 

866 

867 Example: 

868 >>> new_dtensor = dtensor.redistribute(mesh, [Replicate(), Shard(1)]) 

869 >>> new_dtensor = dtensor.redistribute(mesh, ("None", "tp")) 

870 """ 

871 logger.debug( 

872 "redistribute: shape=%s, src_placements=%s -> dst_placements=%s, " 

873 "mesh_shape=%s, local_shape=%s", 

874 tuple(self.shape), 

875 tuple(self._placements), 

876 tuple(placements), 

877 tuple(device_mesh.shape), 

878 tuple(self._local_tensor.shape), 

879 ) 

880 

881 # Build dst_layout from device_mesh and placements 

882 dst_layout = _build_layout( 

883 device_mesh, placements, len(self._global_shape) 

884 ) 

885 

886 # pylint: disable=C0415 

887 from hyper_parallel.core.dtensor.tensor_redistribution import _tensor_redistribution 

888 out = _tensor_redistribution.redistribution(self, dst_layout) 

889 return out 

890 

891 def reduce_partial(self) -> 'DTensor': 

892 """ 

893 Reduce partial sharding state for this DTensor. 

894 

895 Returns: 

896 DTensor: A new DTensor with partial state reduced. 

897 """ 

898 if not self._layout: 

899 return self 

900 to_layout = cp.deepcopy(self._layout) 

901 to_layout.reset_partial() 

902 # pylint: disable=C0415 

903 from hyper_parallel.core.dtensor.tensor_redistribution import _tensor_redistribution 

904 out = _tensor_redistribution.reduce_partial(self, to_layout) 

905 return out 

906 

907 def full_tensor(self) -> Tensor: 

908 """ 

909 Return the full tensor of this DTensor. 

910 

911 Returns: 

912 Tensor: A Tensor object that represents the full tensor of this DTensor. 

913 The returned tensor contains the complete data gathered from 

914 all ranks. 

915 

916 Note: 

917 This operation involves communication across all ranks in the DeviceMesh, 

918 which may be expensive for large tensors. Use with caution in 

919 performance-critical code paths. 

920 

921 Example: 

922 >>> # Assume dtensor is sharded across multiple devices 

923 >>> local_tensor = dtensor.to_local() # Returns only the local shard 

924 >>> full_tensor = dtensor.full_tensor() # Returns the complete tensor 

925 """ 

926 if not self._layout: 

927 return self._local_tensor 

928 

929 # Create a fully replicated layout 

930 replicated_layout = cp.deepcopy(self._layout) 

931 

932 # Set all placements to Replicate and convert to tensor_map 

933 replicated_placements = [Replicate()] * len(replicated_layout.mesh_shape) 

934 replicated_layout.set_placements(replicated_placements) 

935 replicated_layout.placement_to_tensor_map(len(self._global_shape)) 

936 

937 # Clear partial status from original layout since Replicate has no partial 

938 replicated_layout.reset_partial() 

939 

940 # Redistribute to the replicated layout and return local tensor 

941 # pylint: disable=C0415 

942 from hyper_parallel.core.dtensor.tensor_redistribution import _tensor_redistribution 

943 out = _tensor_redistribution.redistribution(self, replicated_layout) 

944 return out.to_local() 

945 

946 

947def _normalize_shard_dim(dim: int, ndim: int) -> int: 

948 return dim + ndim if dim < 0 else dim 

949 

950 

951def _distribute_tensor_with_communication( 

952 tensor: Tensor, 

953 device_mesh: DeviceMesh, 

954 placements: Sequence[Placement], 

955 src_data_rank: int, 

956) -> Tensor: 

957 """Scatter/broadcast a logical global tensor along mesh dimensions (PyTorch parity).""" 

958 local = tensor 

959 if len(placements) < device_mesh.ndim: 

960 raise ValueError( 

961 f"placements length ({len(placements)}) must be at least device_mesh.ndim " 

962 f"({device_mesh.ndim}) when src_data_rank is set" 

963 ) 

964 for mesh_dim in range(device_mesh.ndim): 

965 placement = placements[mesh_dim] 

966 if isinstance(placement, StridedShard): 

967 raise NotImplementedError( 

968 "distribute_tensor with src_data_rank does not support StridedShard yet; " 

969 "pass src_data_rank=None for local-only sharding." 

970 ) 

971 if placement.is_shard(): 

972 shard_dim = _normalize_shard_dim(placement.dim, local.ndim) 

973 num_chunks = device_mesh.size(mesh_dim) 

974 if num_chunks <= 0: 

975 raise ValueError(f"invalid mesh dim size {num_chunks} on mesh_dim={mesh_dim}") 

976 chunks = tuple(local.chunk(num_chunks, dim=shard_dim)) 

977 if not chunks: 

978 raise ValueError(f"cannot shard dim {shard_dim} into {num_chunks} chunks") 

979 output = platform.empty_like(chunks[0]) 

980 local = mesh_scatter(output, chunks, device_mesh, mesh_dim, group_src=src_data_rank) 

981 elif placement.is_replicate() or placement.is_partial(): 

982 local = mesh_broadcast(local, device_mesh, mesh_dim, group_src=src_data_rank) 

983 if isinstance(placement, Partial): 

984 warnings.warn( 

985 f"Partial placement {placement} during distribute_tensor: " 

986 "broadcast only; partial partition is not applied yet.", 

987 stacklevel=3, 

988 ) 

989 else: 

990 raise RuntimeError( 

991 f"unsupported placement {placement} on device mesh dimension {mesh_dim}" 

992 ) 

993 return local 

994 

995 

996def distribute_tensor( 

997 tensor: Tensor, 

998 device_mesh: DeviceMesh, 

999 placements: Union[Sequence[Placement], Sequence[Union[str, Tuple[str, ...]]]], 

1000 *, 

1001 src_data_rank: Optional[int] = None, 

1002) -> DTensor: 

1003 """ 

1004 Distribute a global tensor to the device mesh according to the placements. 

1005 

1006 Args: 

1007 tensor (Tensor): The global tensor to be distributed. All ranks 

1008 should have the same tensor data. 

1009 device_mesh (DeviceMesh): The device mesh describing the device topology. 

1010 placements: The placement strategy. Supports two styles: 

1011 - Placement objects (e.g., ``[Shard(0), Replicate()]``). 

1012 - Alias strings (e.g., ``("dp", "None")`` or 

1013 ``(("dp", "tp"), "None")``), length must equal the number of 

1014 tensor dimensions. 

1015 

1016 Returns: 

1017 DTensor: A new DTensor with the local shard on each rank. 

1018 

1019 Note: 

1020 When ``src_data_rank`` is an ``int`` (e.g. ``0``), shard/replicate 

1021 placements use scatter/broadcast from the source rank on each mesh axis, 

1022 matching PyTorch ``distribute_tensor``. When ``src_data_rank=None`` 

1023 (default), each rank slices its local tensor without communication 

1024 (legacy Hyper behavior; all ranks must hold the same global data). 

1025 

1026 Example: 

1027 >>> mesh = init_device_mesh(device_type="npu", mesh_shape=(2, 2), mesh_dim_names=("dp", "tp")) 

1028 >>> global_tensor = Tensor(np.arange(16).reshape(4, 4)) 

1029 >>> dtensor = distribute_tensor(global_tensor, mesh, [Shard(0), Replicate()]) 

1030 >>> dtensor = distribute_tensor(global_tensor, mesh, ("dp", "None")) 

1031 """ 

1032 layout = _build_layout(device_mesh, placements, len(tensor.shape)) 

1033 if _layout_has_ragged_shard(layout): 

1034 if src_data_rank is None: 

1035 local_tensor = _slice_ragged_tensor(tensor, layout) 

1036 else: 

1037 local_tensor = _scatter_ragged_tensor(tensor, layout, src_data_rank) 

1038 elif src_data_rank is None: 

1039 local_tensor = _get_slice_tensor_by_layout(tensor, layout) 

1040 else: 

1041 local_tensor = _distribute_tensor_with_communication( 

1042 tensor, device_mesh, layout.placements, src_data_rank 

1043 ) 

1044 return DTensor.from_local_with_layout( 

1045 local_tensor, 

1046 layout, 

1047 shape=tuple(tensor.shape), 

1048 ) 

1049 

1050 

1051def _distribute_module_param_source(param: Any) -> Tensor: 

1052 """Tensor data used as the global tensor for :func:`distribute_tensor` (PyTorch uses ``param.data``).""" 

1053 if hasattr(param, "data"): 

1054 return param.data 

1055 return platform.get_param_local_data(param) 

1056 

1057 

1058def _distribute_module_new_parameter(key: str, dtensor: DTensor, requires_grad: bool) -> Any: 

1059 """Build a framework :class:`Parameter` holding *dtensor* (Torch vs MindSpore kwargs differ).""" 

1060 if platform.platform_type == PlatformType.MINDSPORE: 

1061 return platform.Parameter(dtensor, name=key, requires_grad=requires_grad) 

1062 return platform.Parameter(dtensor, requires_grad=requires_grad) 

1063 

1064 

1065def _distribute_module_set_param(module: Any, key: str, new_param: Any) -> None: 

1066 """Register or assign a parameter on *module* (``nn.Module`` or MindSpore ``Cell``).""" 

1067 if hasattr(module, "register_parameter"): 

1068 module.register_parameter(key, new_param) 

1069 return 

1070 if hasattr(module, "_params"): 

1071 module._params[key] = new_param 

1072 if hasattr(module, "_params_list"): 

1073 module._params_list[key] = new_param 

1074 if key in module.__dict__: 

1075 module.__dict__[key] = new_param 

1076 return 

1077 raise TypeError( 

1078 f"distribute_module expects nn.Module-like objects with register_parameter or _params; " 

1079 f"got {type(module)}." 

1080 ) 

1081 

1082 

1083def _distribute_module_iter_params(module: Any) -> list: 

1084 """Return ``[(name, param), ...]`` for direct parameters (``_parameters`` or ``_params``).""" 

1085 if hasattr(module, "_parameters"): 

1086 return list(module._parameters.items()) 

1087 if hasattr(module, "_params"): 

1088 return list(module._params.items()) 

1089 return [] 

1090 

1091 

1092def _distribute_module_iter_buffers(module: Any) -> list: 

1093 """Return ``[(name, buffer), ...]`` if the module has ``_buffers`` (PyTorch ``nn.Module``).""" 

1094 if hasattr(module, "_buffers"): 

1095 return list(module._buffers.items()) 

1096 return [] 

1097 

1098 

1099def _distribute_module_named_modules(module: Any): 

1100 """``nn.Module.named_modules`` or MindSpore ``Cell.cells_and_names`` (submodule FQNs).""" 

1101 if hasattr(module, "named_modules"): 

1102 return module.named_modules() 

1103 if hasattr(module, "cells_and_names"): 

1104 return module.cells_and_names() 

1105 raise TypeError( 

1106 f"distribute_module expects module-like objects with named_modules or cells_and_names; " 

1107 f"got {type(module)}." 

1108 ) 

1109 

1110 

1111def _distribute_module_named_parameters(module: Any): 

1112 """``nn.Module.named_parameters(recurse=False)`` or MindSpore ``Cell.parameters_and_names(expand=False)``.""" 

1113 if hasattr(module, "named_parameters"): 

1114 return module.named_parameters(recurse=False) 

1115 if hasattr(module, "parameters_and_names"): 

1116 return module.parameters_and_names(expand=False) 

1117 raise TypeError( 

1118 f"distribute_module expects module-like objects with named_parameters or parameters_and_names; " 

1119 f"got {type(module)}." 

1120 ) 

1121 

1122 

1123def _replicate_submodule_params_buffers( 

1124 sub_mod: Any, 

1125 device_mesh: DeviceMesh, 

1126 *, 

1127 module_prefix: str = "", 

1128) -> None: 

1129 """Convert plain params/buffers on *sub_mod* to fully replicated :class:`DTensor`.""" 

1130 full_replicate = [Replicate()] * device_mesh.ndim 

1131 for key, param in _distribute_module_iter_params(sub_mod): 

1132 if param is None or isinstance(param, DTensorBase): 

1133 continue 

1134 src = _distribute_module_param_source(param) 

1135 requires_grad = bool(getattr(param, "requires_grad", True)) 

1136 dt = distribute_tensor(src, device_mesh, full_replicate) 

1137 param_name = f"{module_prefix}.{key}" if module_prefix else key 

1138 new_param = _distribute_module_new_parameter(param_name, dt, requires_grad) 

1139 _distribute_module_set_param(sub_mod, key, new_param) 

1140 for key, buffer in _distribute_module_iter_buffers(sub_mod): 

1141 if buffer is None or isinstance(buffer, DTensorBase): 

1142 continue 

1143 sub_mod._buffers[key] = distribute_tensor(buffer, device_mesh, full_replicate) 

1144 

1145 

1146def _distribute_module_run_partition_and_replicate( 

1147 module: Any, 

1148 device_mesh: DeviceMesh, 

1149 partition_fn: Optional[Callable[[str, Any, DeviceMesh], None]], 

1150) -> None: 

1151 """Call optional ``partition_fn`` per ``named_modules`` and replicate remaining tensors.""" 

1152 if partition_fn is None: 

1153 for mod_name, submod in _distribute_module_named_modules(module): 

1154 _replicate_submodule_params_buffers(submod, device_mesh, module_prefix=mod_name) 

1155 return 

1156 for mod_name, submod in _distribute_module_named_modules(module): 

1157 partition_fn(mod_name, submod, device_mesh) 

1158 _replicate_submodule_params_buffers(submod, device_mesh, module_prefix=mod_name) 

1159 

1160 

1161def _distribute_module_register_input_fn( 

1162 module: Any, 

1163 device_mesh: DeviceMesh, 

1164 input_fn: Callable[..., Any], 

1165) -> None: 

1166 """Register *input_fn* as a forward pre-hook on *module* (2- or 3-arg, PyTorch-compatible).""" 

1167 num_args = len(inspect.signature(input_fn).parameters) 

1168 if num_args == 2: 

1169 warnings.warn( 

1170 "Deprecating input_fn that takes two arguments (inputs, device_mesh), " 

1171 "please use input_fn that takes in (module, inputs, device_mesh) instead!", 

1172 FutureWarning, 

1173 stacklevel=3, 

1174 ) 

1175 module.register_forward_pre_hook( 

1176 lambda _, inputs: input_fn(inputs, device_mesh) 

1177 ) 

1178 elif num_args == 3: 

1179 module.register_forward_pre_hook( 

1180 lambda mod, inputs: input_fn(mod, inputs, device_mesh) 

1181 ) 

1182 else: 

1183 raise ValueError( 

1184 f"input_fn should take in 2 or 3 arguments, but got {num_args} arguments!" 

1185 ) 

1186 

1187 

1188def _distribute_module_register_output_fn( 

1189 module: Any, 

1190 device_mesh: DeviceMesh, 

1191 output_fn: Callable[..., Any], 

1192) -> None: 

1193 """Register *output_fn* as a forward hook on *module* (2- or 3-arg, PyTorch-compatible).""" 

1194 num_args = len(inspect.signature(output_fn).parameters) 

1195 if num_args == 2: 

1196 warnings.warn( 

1197 "Deprecating output_fn that takes two arguments (outputs, device_mesh), " 

1198 "please use output_fn that takes in (module, outputs, device_mesh) instead!", 

1199 FutureWarning, 

1200 stacklevel=3, 

1201 ) 

1202 module.register_forward_hook( 

1203 lambda mod, inputs, outputs: output_fn(outputs, device_mesh) 

1204 ) 

1205 elif num_args == 3: 

1206 module.register_forward_hook( 

1207 lambda mod, inputs, outputs: output_fn(mod, outputs, device_mesh) 

1208 ) 

1209 else: 

1210 raise ValueError( 

1211 f"output_fn should take in 2 or 3 arguments, but got {num_args} arguments!" 

1212 ) 

1213 

1214 

1215def distribute_module( 

1216 module: Any, 

1217 device_mesh: Optional[DeviceMesh] = None, 

1218 partition_fn: Optional[Callable[[str, Any, DeviceMesh], None]] = None, 

1219 input_fn: Optional[Callable[..., Any]] = None, 

1220 output_fn: Optional[Callable[..., Any]] = None, 

1221) -> Any: 

1222 """PyTorch ``distribute_module`` parity: shard/replicate params and optional I/O hooks. 

1223 

1224 Unsharded parameters and buffers become fully replicated :class:`DTensor` after 

1225 ``partition_fn``. ``input_fn`` / ``output_fn`` attach only to the root *module*. 

1226 

1227 Args: 

1228 module: Root ``nn.Module`` or MindSpore ``Cell`` with compatible APIs. 

1229 device_mesh: Placement mesh; if ``None``, uses ``_mesh_resources.get_current_mesh()``. 

1230 partition_fn: Per ``named_modules`` callback before replicate pass; ``None`` replicates all. 

1231 input_fn: ``(module, inputs, mesh)`` or deprecated ``(inputs, mesh)`` pre-hook. 

1232 output_fn: ``(module, outputs, mesh)`` or deprecated ``(outputs, mesh)`` forward hook. 

1233 

1234 Returns: 

1235 *module* in place, with distributed tensors where applied. 

1236 

1237 Raises: 

1238 RuntimeError: If called twice on the same *module*. 

1239 ValueError: If ``input_fn`` / ``output_fn`` arity is not 2 or 3. 

1240 

1241 Note: 

1242 XLA / ``torch_xla`` is not supported; strided device :class:`DTensor` only. 

1243 """ 

1244 if getattr(module, "_distribute_module_applied", False): 

1245 raise RuntimeError( 

1246 "distribute_module should only be called once on a module, " 

1247 "but it has already been called on this module!" 

1248 ) 

1249 device_mesh = device_mesh or _mesh_resources.get_current_mesh() 

1250 _distribute_module_run_partition_and_replicate(module, device_mesh, partition_fn) 

1251 if input_fn is not None: 

1252 _distribute_module_register_input_fn(module, device_mesh, input_fn) 

1253 if output_fn is not None: 

1254 _distribute_module_register_output_fn(module, device_mesh, output_fn) 

1255 module._distribute_module_applied = True 

1256 return module 

1257 

1258 

1259def _dtensor_init_helper( 

1260 init_op, 

1261 size, 

1262 device_mesh, 

1263 placements, 

1264 *, 

1265 rng_tracked: bool = False, 

1266 **kwargs, 

1267) -> DTensor: 

1268 """ 

1269 Helper function to create and initialize a distributed tensor. 

1270 

1271 Args: 

1272 size: Shape of the tensor. 

1273 dtype: Data type of the tensor. 

1274 device: Target device for the tensor. 

1275 requires_grad: Whether the tensor requires gradient. 

1276 rng_tracked: When ``True``, initialize via :class:`OffsetBasedRNGTracker` 

1277 so shard/replicate random semantics match PyTorch DTensor factories. 

1278 

1279 Returns: 

1280 DTensor: The initialized distributed tensor. 

1281 """ 

1282 global_shape = (size,) if isinstance(size, int) else tuple(size) 

1283 layout = _build_layout(device_mesh, placements, len(global_shape)) 

1284 if _layout_has_ragged_shard(layout): 

1285 raise NotImplementedError( 

1286 "RaggedShard tensor factories are not implemented in the DTensor metadata phase" 

1287 ) 

1288 

1289 # get local tensor shape 

1290 local_shape = compute_local_shape_and_global_offset( 

1291 size, device_mesh, placements 

1292 ) 

1293 

1294 # initialize the local tensor 

1295 if init_op is platform.full: 

1296 fill_value = kwargs.pop("fill_value", 0) 

1297 local_tensor = init_op(local_shape, fill_value, **kwargs) 

1298 elif rng_tracked: 

1299 # pylint: disable=C0415 

1300 from hyper_parallel.core.dtensor.random import is_rng_supported_mesh, OffsetBasedRNGTracker 

1301 from hyper_parallel.core.shard._op_dispatch import _OP_DISPATCHER 

1302 

1303 layout = _build_layout(device_mesh, placements, len(local_shape)) 

1304 if is_rng_supported_mesh(device_mesh): 

1305 if _OP_DISPATCHER._rng_tracker is None: 

1306 _OP_DISPATCHER._rng_tracker = OffsetBasedRNGTracker(run_state_sync=False) 

1307 with _OP_DISPATCHER._rng_tracker._distribute_region( 

1308 device_mesh, 

1309 layout.placements, 

1310 global_shape, 

1311 ): 

1312 local_tensor = init_op(local_shape, **kwargs) 

1313 else: 

1314 local_tensor = init_op(local_shape, **kwargs) 

1315 else: 

1316 local_tensor = init_op(local_shape, **kwargs) 

1317 

1318 return DTensor.from_local( 

1319 local_tensor, 

1320 device_mesh, 

1321 placements, 

1322 ) 

1323 

1324 

1325def ones( 

1326 size, 

1327 device_mesh, 

1328 placements, 

1329) -> DTensor: 

1330 """ 

1331 Returns a :class:`DTensor` filled with the scalar value 1, with the shape defined 

1332 by the variable argument ``size``. 

1333 

1334 Args: 

1335 size (Union[tuple[int], list[int], int, Tensor]): The specified shape of output tensor. Only positive integer or 

1336 tuple or Tensor containing positive integers are allowed. If it is a Tensor, 

1337 it must be a 0-D or 1-D Tensor with int32 or int64 dtypes. 

1338 

1339 Keyword args: 

1340 device_mesh: :class:`DeviceMesh` type, contains the mesh info of ranks 

1341 placements: a sequence of :class:`Placement` type: ``Shard``, ``Replicate`` 

1342 

1343 Returns: 

1344 A :class:`DTensor` object on each rank 

1345 """ 

1346 ones_ = platform.ones 

1347 return _dtensor_init_helper( 

1348 ones_, 

1349 size, 

1350 device_mesh=device_mesh, 

1351 placements=placements, 

1352 ) 

1353 

1354 

1355def empty( 

1356 size, 

1357 device_mesh, 

1358 placements, 

1359) -> DTensor: 

1360 """ 

1361 Returns a :class:`DTensor` filled with uninitialized data. The shape of the :class:`DTensor` 

1362 is defined by the variable argument ``size``. 

1363 

1364 Args: 

1365 size (Union[tuple[int], list[int], int]): The specified shape of output tensor. Can be variable numbers of 

1366 positive integers or tuple or list containing positive integers. 

1367 

1368 Keyword args: 

1369 device_mesh: :class:`DeviceMesh` type, contains the mesh info of ranks 

1370 placements: a sequence of :class:`Placement` type: ``Shard``, ``Replicate`` 

1371 

1372 Returns: 

1373 A :class:`DTensor` object on each rank 

1374 """ 

1375 empty_ = platform.empty 

1376 return _dtensor_init_helper( 

1377 empty_, 

1378 size, 

1379 device_mesh=device_mesh, 

1380 placements=placements, 

1381 ) 

1382 

1383 

1384def full( 

1385 size, 

1386 fill_value, 

1387 *, 

1388 device_mesh, 

1389 placements, 

1390) -> DTensor: 

1391 """ 

1392 Returns a :class:`DTensor` filled with ``fill_value`` according to ``device_mesh`` and 

1393 ``placements``, with the shape defined by the argument ``size``. 

1394 

1395 Args: 

1396 size (Union[tuple[int], list[int]]): The specified shape of output tensor. 

1397 fill_value (Union[numbers.Number, Tensor]): Value to fill the returned tensor. It can be a scalar number, a 0-D 

1398 Tensor, or a 1-D Tensor with only one element. 

1399 

1400 Keyword args: 

1401 device_mesh: :class:`DeviceMesh` type, contains the mesh info of ranks. 

1402 placements: a sequence of :class:`Placement` type: ``Shard``, ``Replicate`` 

1403 

1404 Returns: 

1405 A :class:`DTensor` object on each rank 

1406 """ 

1407 full_ = platform.full 

1408 return _dtensor_init_helper( 

1409 full_, 

1410 size, 

1411 fill_value=fill_value, 

1412 device_mesh=device_mesh, 

1413 placements=placements, 

1414 ) 

1415 

1416 

1417def zeros( 

1418 size, 

1419 device_mesh, 

1420 placements, 

1421) -> DTensor: 

1422 """ 

1423 Returns a :class:`DTensor` filled with the scalar value 0. 

1424 

1425 Args: 

1426 size (Union[tuple[int], list[int], int, Tensor]): The specified shape of output tensor. Only positive integer or 

1427 tuple or Tensor containing positive integers are allowed. If it is a Tensor, 

1428 it must be a 0-D or 1-D Tensor with int32 or int64 dtypes. 

1429 Keyword args: 

1430 device_mesh: :class:`DeviceMesh` type, contains the mesh info of ranks 

1431 placements: a sequence of :class:`Placement` type: ``Shard``, ``Replicate`` 

1432 

1433 Returns: 

1434 A :class:`DTensor` object on each rank 

1435 """ 

1436 zeros_ = platform.zeros 

1437 return _dtensor_init_helper( 

1438 zeros_, 

1439 size, 

1440 device_mesh=device_mesh, 

1441 placements=placements, 

1442 ) 

1443 

1444 

1445def rand( 

1446 size, 

1447 device_mesh, 

1448 placements, 

1449 **kwargs, 

1450) -> DTensor: 

1451 """ 

1452 Returns a :class:`DTensor` filled with random numbers from a uniform 

1453 distribution on ``[0, 1)``. 

1454 

1455 Args: 

1456 size: Global output shape. 

1457 device_mesh: :class:`DeviceMesh` for the distributed layout. 

1458 placements: Per-mesh-dimension :class:`Placement` values. 

1459 **kwargs: Forwarded to the platform ``rand`` call (for example ``dtype``). 

1460 

1461 Returns: 

1462 A :class:`DTensor` object on each rank. 

1463 """ 

1464 return _dtensor_init_helper( 

1465 platform.rand, 

1466 size, 

1467 device_mesh=device_mesh, 

1468 placements=placements, 

1469 rng_tracked=True, 

1470 **kwargs, 

1471 ) 

1472 

1473 

1474def randn( 

1475 size, 

1476 device_mesh, 

1477 placements, 

1478 **kwargs, 

1479) -> DTensor: 

1480 """ 

1481 Returns a :class:`DTensor` filled with random numbers from a normal 

1482 distribution with mean ``0`` and variance ``1``. 

1483 

1484 Args: 

1485 size: Global output shape. 

1486 device_mesh: :class:`DeviceMesh` for the distributed layout. 

1487 placements: Per-mesh-dimension :class:`Placement` values. 

1488 **kwargs: Forwarded to the platform ``randn`` call (for example ``dtype``). 

1489 

1490 Returns: 

1491 A :class:`DTensor` object on each rank. 

1492 """ 

1493 return _dtensor_init_helper( 

1494 platform.randn, 

1495 size, 

1496 device_mesh=device_mesh, 

1497 placements=placements, 

1498 rng_tracked=True, 

1499 **kwargs, 

1500 )