Coverage for  / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / platform / platform.py: 66%

428 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-08-04 05:18 +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"""framework platform api""" 

16import os 

17from datetime import timedelta 

18from enum import auto, Enum 

19from typing import Optional, Any, Union 

20 

21import numpy as np 

22 

23# Environment variable name used to specify the AI framework platform to use 

24HYPER_PARALLEL_PLATFORM = "HYPER_PARALLEL_PLATFORM" 

25 

26# Identifier for the MindSpore framework 

27HYPER_PARALLEL_PLATFORM_MINDSPORE = "mindspore" 

28 

29# Identifier for the PyTorch framework 

30HYPER_PARALLEL_PLATFORM_TORCH = "torch" 

31 

32 

33class AsyncHandle: 

34 """Idempotent wait handle for an async collective operation. 

35 

36 Wraps the async tensor returned by 

37 :meth:`Platform.differentiable_all_to_all_single_async` and provides a 

38 :meth:`wait` method that is safe to call multiple times. 

39 """ 

40 

41 def __init__(self, async_tensor) -> None: 

42 self._tensor = async_tensor 

43 self._waited = False 

44 

45 def wait(self): 

46 """Wait for the async collective to complete. 

47 

48 Idempotent — the first call blocks until the collective finishes; 

49 subsequent calls are no-ops. 

50 

51 Returns: 

52 The now-materialised result tensor. 

53 """ 

54 if not self._waited: 

55 get_platform().wait_async_tensor(self._tensor) 

56 self._waited = True 

57 return self._tensor 

58 

59 

60class PlatformType(Enum): 

61 """Enumeration class for AI framework platform types. 

62 

63 Used to identify different deep learning framework platform types. 

64 """ 

65 MINDSPORE = auto() 

66 PYTORCH = auto() 

67 

68 

69# Global platform instance, used to cache the created platform object 

70platform = None 

71 

72 

73def get_mindspore_platform(): 

74 """Create and return a MindSpore platform instance. 

75 

76 Returns: 

77 MindSporePlatform: A MindSpore platform instance. 

78 """ 

79 # pylint: disable=C0415 

80 from hyper_parallel.platform.mindspore.platform import MindSporePlatform 

81 global platform 

82 platform = MindSporePlatform() 

83 return platform 

84 

85 

86def get_torch_platform(): 

87 """Create and return a PyTorch platform instance. 

88 

89 Returns: 

90 TorchPlatform: A PyTorch platform instance. 

91 """ 

92 # pylint: disable=C0415 

93 from hyper_parallel.platform.torch.platform import TorchPlatform 

94 global platform 

95 platform = TorchPlatform() 

96 return platform 

97 

98 

99def get_platform(): 

100 """Obtain a framework platform instance. 

101 

102 Returns the appropriate AI framework platform instance based on environment variables or a default priority order. 

103 The lookup priority is as follows: 

104 1. Platform specified by environment variable 

105 2. MindSpore platform (default preferred choice) 

106 3. PyTorch platform (fallback option) 

107 

108 Returns: 

109 Platform: An instance of the framework platform 

110 

111 Raises: 

112 ImportError: Raised when none of the supported frameworks are available 

113 """ 

114 if platform is not None: 

115 return platform 

116 platform_type = os.environ.get(HYPER_PARALLEL_PLATFORM) 

117 if platform_type is not None and isinstance(platform_type, str): 

118 platform_type = platform_type.lower() 

119 if platform_type == HYPER_PARALLEL_PLATFORM_MINDSPORE: 

120 return get_mindspore_platform() 

121 if platform_type == HYPER_PARALLEL_PLATFORM_TORCH: 

122 return get_torch_platform() 

123 try: 

124 return get_mindspore_platform() 

125 except ImportError: 

126 return get_torch_platform() 

127 

128 

129EXISTING_COMM_GROUPS = {} 

130 

131 

132class Platform: 

133 """Platform api""" 

134 current_grad_handle = None 

135 post_grad_handle_process = None 

136 grad_sync_stream = None 

137 

138 @property 

139 def custom_ops(self): 

140 """Return the platform-specific custom ops interface. 

141 

142 Subclasses MUST override this property to return an object that 

143 exposes the platform-specific custom operator implementations. 

144 

145 Returns: 

146 object: Platform-specific custom ops class instance. 

147 """ 

148 raise NotImplementedError( 

149 "Platform subclasses must implement custom_ops" 

150 ) 

151 

152 @staticmethod 

153 def get_rank(): 

154 """Get the rank of the current process in the default process group. 

155 

156 Returns: 

157 int: The rank of the current process. 

158 """ 

159 raise NotImplementedError("Platform subclasses must implement get_rank") 

160 

161 @staticmethod 

162 def get_global_rank(group, group_rank): 

163 """Convert a group rank to its global rank. 

164 

165 Args: 

166 group: The process group to query. 

167 group_rank (int): The rank within the group. 

168 

169 Returns: 

170 int: The global rank corresponding to the group rank. 

171 """ 

172 raise NotImplementedError("Platform subclasses must implement get_global_rank") 

173 

174 @staticmethod 

175 def get_group_rank(group): 

176 """Return this process's rank within *group*.""" 

177 raise NotImplementedError("Platform subclasses must implement get_group_rank") 

178 

179 @staticmethod 

180 def get_world_size(): 

181 """Get the total number of processes in the default process group. 

182 

183 Returns: 

184 int: The world size (total number of processes). 

185 """ 

186 raise NotImplementedError("Platform subclasses must implement get_world_size") 

187 

188 @staticmethod 

189 def get_op_name(func): 

190 """Get the canonical name of an operator function. 

191 

192 Args: 

193 func: The operator function to query. 

194 

195 Returns: 

196 str: The canonical name of the operator. 

197 """ 

198 raise NotImplementedError("Platform subclasses must implement get_op_name") 

199 

200 @staticmethod 

201 def differentiable_all_gather_concat(data, group, concat_size, concat_dim, rank_list=None): 

202 """Perform differentiable all-gather and concatenate tensors along a dimension. 

203 

204 Args: 

205 data: The input tensor to gather. 

206 group: The process group for collective communication. 

207 concat_size (int): The size to concatenate along concat_dim. 

208 concat_dim (int): The dimension along which to concatenate. 

209 rank_list: Optional rank order expected by the logical layout. 

210 

211 Returns: 

212 The concatenated tensor after all-gather operation. 

213 """ 

214 raise NotImplementedError("Platform subclasses must implement differentiable_all_gather_concat") 

215 

216 @staticmethod 

217 def chunk(data, split_dim, split_size, index): 

218 """Split tensor along a dimension and return the chunk at the given index. 

219 

220 Args: 

221 data: The input tensor to split. 

222 split_dim (int): The dimension along which to split. 

223 split_size (int): The size of each split chunk. 

224 index (int): The index of the chunk to return. 

225 

226 Returns: 

227 The tensor chunk at the specified index. 

228 """ 

229 raise NotImplementedError("Platform subclasses must implement chunk") 

230 

231 @staticmethod 

232 def differentiable_all_to_all(input_data, output_shape, group): 

233 """Perform differentiable all-to-all communication. 

234 

235 Args: 

236 input_data: The input tensor to redistribute. 

237 output_shape: The shape of the output tensor. 

238 group: The process group for collective communication. 

239 

240 Returns: 

241 The output tensor after all-to-all operation. 

242 """ 

243 raise NotImplementedError("Platform subclasses must implement differentiable_all_to_all") 

244 

245 @staticmethod 

246 def tensor_type_cast(input_data, cast_type): 

247 """Cast tensor to a specified dtype. 

248 

249 Args: 

250 input_data: The input tensor to cast. 

251 cast_type: The target dtype to cast to. 

252 

253 Returns: 

254 The tensor cast to the specified dtype. 

255 """ 

256 raise NotImplementedError("Platform subclasses must implement tensor_type_cast") 

257 

258 @staticmethod 

259 def is_tensor(obj: Any) -> bool: 

260 """Return True if ``obj`` is this framework's tensor type.""" 

261 raise NotImplementedError("Platform subclasses must implement is_tensor") 

262 

263 @staticmethod 

264 def get_tensor_storage_size(tensor: Any) -> int: 

265 """Return serialized byte size (numel * element size) for this framework's tensor.""" 

266 raise NotImplementedError("Platform subclasses must implement get_tensor_storage_size") 

267 

268 @staticmethod 

269 def differentiable_all_reduce(data, op, group): 

270 """Perform differentiable all-reduce operation. 

271 

272 Args: 

273 data: The input tensor to reduce. 

274 op: The reduction operation (e.g., sum, max, min). 

275 group: The process group for collective communication. 

276 

277 Returns: 

278 The reduced tensor with gradients supported. 

279 """ 

280 raise NotImplementedError("Platform subclasses must implement differentiable_all_reduce") 

281 

282 @staticmethod 

283 def differentiable_reduce_scatter(data, dev_num, axis, op, group): 

284 """Perform differentiable reduce-scatter operation. 

285 

286 Args: 

287 data: The input tensor to reduce and scatter. 

288 dev_num (int): The number of devices to scatter across. 

289 axis (int): The axis along which to scatter. 

290 op: The reduction operation (e.g., sum, max, min). 

291 group: The process group for collective communication. 

292 

293 Returns: 

294 The scattered tensor chunk with gradients supported. 

295 """ 

296 raise NotImplementedError("Platform subclasses must implement differentiable_reduce_scatter") 

297 

298 @staticmethod 

299 def init_parameters(module, stage_index): 

300 """Initialize parameters for a module at a specific pipeline stage. 

301 

302 This method is primarily needed for MindSpore platform which requires 

303 explicit parameter initialization interface. 

304 

305 Args: 

306 module: The module whose parameters need to be initialized. 

307 stage_index (int): The pipeline stage index for the module. 

308 

309 Raises: 

310 ValueError: If module is None or stage_index is negative. 

311 """ 

312 if module is None: 

313 raise ValueError("input module must not be none.") 

314 if stage_index < 0: 

315 raise ValueError("input stage_index must be positive.") 

316 

317 @staticmethod 

318 def get_cell_construct(cell): 

319 """Get the construct (forward) function of a cell/module. 

320 

321 Args: 

322 cell: The cell or module to get the construct function from. 

323 

324 Returns: 

325 The construct/forward callable of the cell. 

326 """ 

327 raise NotImplementedError("Platform subclasses must implement get_cell_construct") 

328 

329 @staticmethod 

330 def get_cells_and_names(cell): 

331 """Get all nested cells/modules and their names. 

332 

333 Args: 

334 cell: The root cell or module to traverse. 

335 

336 Returns: 

337 list: A list of tuples containing (name, cell) pairs. 

338 """ 

339 raise NotImplementedError("Platform subclasses must implement get_cells_and_names") 

340 

341 @staticmethod 

342 def get_modules(module): 

343 """Return all sub-modules contained in the given module.""" 

344 raise NotImplementedError("Platform subclasses must implement get_modules") 

345 

346 @staticmethod 

347 def search_parameter_by_name(cell, param_name: str): 

348 """Search for a parameter by name within a cell/module. 

349 

350 Args: 

351 cell: The cell or module to search in. 

352 param_name (str): The name of the parameter to find. 

353 

354 Returns: 

355 The parameter if found, otherwise None. 

356 """ 

357 raise NotImplementedError("Platform subclasses must implement search_parameter_by_name") 

358 

359 @staticmethod 

360 def update_parameter_by_name(cell, result: tuple, new_param) -> bool: 

361 """Update a parameter by name within a cell/module. 

362 

363 Args: 

364 cell: The cell or module containing the parameter. 

365 result (tuple): A tuple containing (param_name, parameter) to update. 

366 new_param: The new parameter value to set. 

367 

368 Returns: 

369 bool: True if update was successful, False otherwise. 

370 """ 

371 raise NotImplementedError("Platform subclasses must implement update_parameter_by_name") 

372 

373 @staticmethod 

374 def set_layout_into_parameter(param, layout): 

375 """Attach a DTensor layout to a parameter. 

376 

377 Args: 

378 param: The parameter to attach the layout to. 

379 layout: The DTensor layout describing tensor distribution. 

380 """ 

381 raise NotImplementedError("Platform subclasses must implement set_layout_into_parameter") 

382 

383 @staticmethod 

384 def get_param_local_shape(param): 

385 """Get the local shape of a distributed parameter. 

386 

387 Args: 

388 param: The parameter to query. 

389 

390 Returns: 

391 tuple: The local shape of the parameter shard. 

392 """ 

393 raise NotImplementedError("Platform subclasses must implement get_param_local_shape") 

394 

395 @staticmethod 

396 def get_param_local_data(param): 

397 """Get the local data tensor of a distributed parameter. 

398 

399 Args: 

400 param: The parameter to query. 

401 

402 Returns: 

403 The local tensor data of the parameter shard. 

404 """ 

405 raise NotImplementedError("Platform subclasses must implement get_param_local_data") 

406 

407 @staticmethod 

408 def update_param_data(param, data): 

409 """Update the data of a parameter with new tensor data. 

410 

411 Args: 

412 param: The parameter to update. 

413 data: The new tensor data to assign. 

414 """ 

415 raise NotImplementedError("Platform subclasses must implement update_param_data") 

416 

417 @staticmethod 

418 def get_param_type_size(param): 

419 """Get the size in bytes of a parameter's dtype. 

420 

421 Args: 

422 param: The parameter to query. 

423 

424 Returns: 

425 int: The size in bytes of the parameter's data type. 

426 """ 

427 raise NotImplementedError("Platform subclasses must implement get_param_type_size") 

428 

429 @staticmethod 

430 def new_zero_parameter(param_shape, param_type, requires_grad, device): 

431 """Create a new parameter initialized with zeros. 

432 

433 Args: 

434 param_shape (tuple): The shape of the parameter. 

435 param_type: The dtype of the parameter. 

436 requires_grad (bool): Whether the parameter requires gradients. 

437 device: The device on which to create the parameter. 

438 

439 Returns: 

440 A new parameter tensor filled with zeros. 

441 """ 

442 raise NotImplementedError("Platform subclasses must implement new_zero_parameter") 

443 

444 @staticmethod 

445 def new_tensor(tensor_shape, tensor_type, device): 

446 """Create a new tensor with the specified shape, dtype, and device. 

447 

448 Args: 

449 tensor_shape (tuple): The shape of the tensor. 

450 tensor_type: The dtype of the tensor. 

451 device: The device on which to create the tensor. 

452 

453 Returns: 

454 A new tensor with uninitialized values. 

455 """ 

456 raise NotImplementedError("Platform subclasses must implement new_tensor") 

457 

458 @staticmethod 

459 def full_like(tensor, fill_value, dtype=None): 

460 """Create a tensor filled with a value, with same shape as input. 

461 

462 Args: 

463 tensor: The input tensor to copy shape from. 

464 fill_value: The value to fill the new tensor with. 

465 dtype: Optional dtype for the new tensor. If None, uses input tensor's dtype. 

466 

467 Returns: 

468 A new tensor filled with the specified value. 

469 """ 

470 raise NotImplementedError("Platform subclasses must implement full_like") 

471 

472 @staticmethod 

473 def set_tensor_requires_grad(input_tensor): 

474 """Enable gradient tracking for a tensor in-place. 

475 

476 Args: 

477 input_tensor: The tensor to enable gradients for. 

478 

479 Returns: 

480 The same tensor with requires_grad set to True. 

481 """ 

482 raise NotImplementedError("Platform subclasses must implement set_tensor_requires_grad") 

483 

484 @staticmethod 

485 def all_gather_into_tensor(data, group_info, async_op=False): 

486 """Gather tensors from all ranks into a single output tensor. 

487 

488 Args: 

489 data: The input tensor to gather. 

490 group_info: The process group for collective communication. 

491 async_op (bool): If True, returns a work handle for async operation. 

492 

493 Returns: 

494 The gathered tensor, or a tuple of (tensor, handle) if async_op is True. 

495 """ 

496 raise NotImplementedError("Platform subclasses must implement all_gather_into_tensor") 

497 

498 @staticmethod 

499 def all_reduce(data, group_info, async_op=False): 

500 """Reduce tensors across all ranks using specified operation. 

501 

502 Args: 

503 data: The input tensor to reduce. 

504 group_info: The process group for collective communication. 

505 async_op (bool): If True, returns a work handle for async operation. 

506 

507 Returns: 

508 The reduced tensor, or a tuple of (tensor, handle) if async_op is True. 

509 """ 

510 raise NotImplementedError("Platform subclasses must implement all_reduce") 

511 

512 @staticmethod 

513 def broadcast(data, src=None, group=None, async_op=False, group_src=None): 

514 """Broadcast tensor from source rank to all ranks in group.""" 

515 raise NotImplementedError("Platform subclasses must implement broadcast") 

516 

517 @staticmethod 

518 def scatter(output, scatter_list, src=None, group=None, async_op=False, group_src=None): 

519 """Scatter tensor list from source rank to all ranks in group.""" 

520 raise NotImplementedError("Platform subclasses must implement scatter") 

521 

522 @staticmethod 

523 def isend(tensor, dst=None, group=None, tag=0): 

524 """Send tensor asynchronously to destination rank. 

525 

526 Args: 

527 tensor: The tensor to send. 

528 dst (int, optional): The destination rank. Defaults to None. 

529 group: The process group for communication. Defaults to None. 

530 tag (int): A tag to identify the send operation. Defaults to 0. 

531 

532 Returns: 

533 A work handle that can be waited on. 

534 """ 

535 raise NotImplementedError("Platform subclasses must implement isend") 

536 

537 @staticmethod 

538 def irecv(tensor, src=None, group=None, tag=0): 

539 """Receive tensor asynchronously from source rank. 

540 

541 Args: 

542 tensor: The tensor buffer to receive data into. 

543 src (int, optional): The source rank. Defaults to None. 

544 group: The process group for communication. Defaults to None. 

545 tag (int): A tag to identify the receive operation. Defaults to 0. 

546 

547 Returns: 

548 A work handle that can be waited on. 

549 """ 

550 raise NotImplementedError("Platform subclasses must implement irecv") 

551 

552 @staticmethod 

553 def p2p_op(op_type, tensor, peer, group=None): 

554 """Build a batched-P2P descriptor (no launch). 

555 

556 Returns an opaque object understood by :meth:`batch_isend_irecv`. 

557 Lets callers assemble a mixed send/recv batch that the backend can 

558 run concurrently (e.g. TX/RX duplex on one link) in a single op. 

559 

560 Args: 

561 op_type (str): ``"isend"`` or ``"irecv"``. 

562 tensor: Tensor to send, or the buffer to receive into. 

563 peer (int): Global rank of the peer. 

564 group: Process group. ``None`` uses the default group. 

565 

566 Returns: 

567 A backend P2P-op descriptor. 

568 """ 

569 raise NotImplementedError("Platform subclasses must implement p2p_op") 

570 

571 @staticmethod 

572 def batch_isend_irecv(p2p_ops): 

573 """Launch a batch of :meth:`p2p_op` descriptors as one async op. 

574 

575 The whole batch shares a single completion handle (the backend runs 

576 the items concurrently on one comm stream), so a send and a recv to 

577 the same peer overlap on the duplex link. 

578 

579 Args: 

580 p2p_ops (list): Descriptors from :meth:`p2p_op`. 

581 

582 Returns: 

583 A single work handle covering the whole batch, or ``None`` when 

584 ``p2p_ops`` is empty. 

585 """ 

586 raise NotImplementedError("Platform subclasses must implement batch_isend_irecv") 

587 

588 @staticmethod 

589 def prepare_batch_p2p_group(group: Any = None) -> None: 

590 """Prepare a process group before its first batched P2P operation. 

591 

592 Backends that require full-group participation before subset batched 

593 P2P should synchronize the group here. Other backends may implement 

594 this as a no-op. 

595 

596 Args: 

597 group: The process group used by the batched P2P operations. 

598 ``None`` uses the default group. 

599 """ 

600 raise NotImplementedError("Platform subclasses must implement prepare_batch_p2p_group") 

601 

602 @staticmethod 

603 def p2p_exchange(tensor, peer_rank: int, group=None): 

604 """Differentiable symmetric P2P exchange (send local tensor, receive peer's tensor). 

605 

606 Sends ``tensor`` to ``peer_rank`` and simultaneously receives the peer's 

607 tensor. The operation is differentiable: the backward pass performs the 

608 same symmetric exchange on the upstream gradient. 

609 

610 Args: 

611 tensor: Local tensor to send. 

612 peer_rank (int): Global rank of the communication peer. 

613 group: Process group. ``None`` uses the default group. 

614 

615 Returns: 

616 Tensor received from ``peer_rank``, with the same shape and dtype as 

617 the input ``tensor``. 

618 """ 

619 raise NotImplementedError("Platform subclasses must implement p2p_exchange") 

620 

621 @staticmethod 

622 def send_object_list(obj_list, dst=None, group=None): 

623 """Send a list of Python objects to destination rank. 

624 

625 Args: 

626 obj_list (list): The list of Python objects to send. 

627 dst (int, optional): The destination rank. Defaults to None. 

628 group: The process group for communication. Defaults to None. 

629 """ 

630 raise NotImplementedError("Platform subclasses must implement send_object_list") 

631 

632 @staticmethod 

633 def recv_object_list(obj_list, src=None, group=None): 

634 """Receive a list of Python objects from source rank. 

635 

636 Args: 

637 obj_list (list): The list buffer to receive objects into. 

638 src (int, optional): The source rank. Defaults to None. 

639 group: The process group for communication. Defaults to None. 

640 """ 

641 raise NotImplementedError("Platform subclasses must implement recv_object_list") 

642 

643 @staticmethod 

644 def reduce_scatter_tensor(data, group_info, async_op=False): 

645 """Reduce and scatter tensor across all ranks in group. 

646 

647 Args: 

648 data: The input tensor to reduce and scatter. 

649 group_info: The process group for collective communication. 

650 async_op (bool): If True, returns a work handle for async operation. 

651 

652 Returns: 

653 The scattered tensor chunk, or a tuple of (tensor, handle) if async_op is True. 

654 """ 

655 raise NotImplementedError("Platform subclasses must implement reduce_scatter_tensor") 

656 

657 @staticmethod 

658 def all_gather_single(input_tensor, output_shape, group, async_op=False): 

659 """All-gather tensor shards with optional async execution. 

660 

661 Args: 

662 input_tensor: Input tensor whose leading dimension is gathered. 

663 output_shape: Shape of the gathered output tensor. 

664 group: Process group (ProcessGroup for torch, group name string for mindspore). 

665 async_op: If True, returns an async work handle. 

666 

667 Returns: 

668 Tuple ``(output, work)`` where *output* is the gathered tensor and 

669 *work* is the async handle (``None`` when ``async_op=False``). 

670 """ 

671 raise NotImplementedError("Platform subclasses must implement all_gather_single") 

672 

673 @staticmethod 

674 def reduce_scatter_single(input_tensor, output_shape, group, async_op=False): 

675 """Reduce-scatter a tensor with optional async execution. 

676 

677 Args: 

678 input_tensor: Input tensor whose leading dimension is split across ranks. 

679 output_shape: Shape of the local reduced output tensor. 

680 group: Process group (ProcessGroup for torch, group name string for mindspore). 

681 async_op: If True, returns an async work handle. 

682 

683 Returns: 

684 Tuple ``(output, work)`` where *output* is the local shard and 

685 *work* is the async handle (``None`` when ``async_op=False``). 

686 """ 

687 raise NotImplementedError("Platform subclasses must implement reduce_scatter_single") 

688 

689 @staticmethod 

690 def all_to_all_single(input_tensor, output_shape, group, async_op=False): 

691 """All-to-all single collective with optional async execution. 

692 

693 Args: 

694 input_tensor: Input tensor to scatter. 

695 output_shape: Shape of the pre-allocated output tensor. 

696 group: Process group (ProcessGroup for torch, group name string for mindspore). 

697 async_op: If True, returns a work handle; the output tensor is 

698 filled only after ``work.wait()`` is called. 

699 

700 Returns: 

701 Tuple ``(output, work)`` where *output* is the result tensor and 

702 *work* is the async handle (``None`` when ``async_op=False``). 

703 

704 Raises: 

705 NotImplementedError: Must be implemented by platform subclasses. 

706 """ 

707 raise NotImplementedError("Platform subclasses must implement all_to_all_single") 

708 

709 @staticmethod 

710 def differentiable_async_allgather_wait(x, work, out_perm, group, world_size, gather_dim, 

711 handle_box=None): 

712 """Differentiable wrapper that waits for a pre-launched async all-gather. 

713 

714 Forward waits for the all-gather handle and reconstructs the tensor by 

715 moving the gathered leading dimension back to ``gather_dim``. 

716 

717 Backward launches the reverse reduce-scatter. If ``handle_box`` is a 

718 mutable list, the reduce-scatter handle is appended there and a zero 

719 gradient is returned to be replaced by the caller's backward pre-hook. 

720 If ``handle_box`` is ``None``, the reduce-scatter is waited immediately 

721 and its local result is returned, preserving composability with an 

722 upstream autograd communication op. 

723 

724 Args: 

725 x: Original input tensor; anchors the op in the autograd graph. 

726 work: Async work handle from all-gather. 

727 out_perm: Output buffer filled by all-gather. 

728 group: Communication group for backward reduce-scatter. 

729 world_size: Group size. 

730 gather_dim: Dimension gathered in forward. 

731 handle_box: Optional mutable list for deferred backward wait. 

732 

733 Returns: 

734 Gathered tensor connected to the autograd graph through *x*. 

735 """ 

736 raise NotImplementedError("Platform subclasses must implement differentiable_async_allgather_wait") 

737 

738 @staticmethod 

739 def differentiable_async_a2a_wait(x, work, out_perm, group, world_size, concat_dim, split_dim, 

740 handle_box=None): 

741 """Differentiable wrapper that waits for a pre-launched async A2A. 

742 

743 Wraps the wait-and-reconstruct step in the platform autograd mechanism 

744 so gradients flow correctly through the all-to-all communication. 

745 

746 The A2A direction is seq→head (forward): the output gathers along 

747 ``concat_dim`` (sequence grows from S/cp to S) and scatters along 

748 ``split_dim`` (heads shrink from H to H/ws). 

749 

750 In backward, launches an async head→seq A2A on the incoming gradient 

751 and appends ``(work, out_perm)`` to ``handle_box`` so the caller can 

752 wait just before the projection GEMM, achieving GEMM–A2A overlap. 

753 

754 Args: 

755 x: Original projection output tensor; anchors the op 

756 in the autograd graph. 

757 work: Async work handle from ``all_to_all_single(async_op=True)``. 

758 out_perm: Output buffer filled once ``work.wait()`` completes 

759 (shape ``[ws, ...]``). 

760 group: Process group for the reverse A2A in backward. 

761 world_size: CP/Ulysses degree. 

762 concat_dim: Dimension that is gathered (concatenated) in forward; 

763 typically the sequence dimension. 

764 split_dim: Dimension that is scattered (split) in forward; 

765 typically the head dimension. 

766 handle_box: Optional mutable list ``[]``. In backward, ``(work, out_perm)`` 

767 for the reverse A2A is appended here so the pre-hook can wait. 

768 

769 Returns: 

770 Result tensor with ``concat_dim`` gathered and ``split_dim`` split, 

771 connected to the autograd graph through *x*. 

772 

773 Raises: 

774 NotImplementedError: Must be implemented by platform subclasses. 

775 """ 

776 raise NotImplementedError("Platform subclasses must implement differentiable_async_a2a_wait") 

777 

778 @staticmethod 

779 def differentiable_sync_hook(x, hook_name: str, coordinator): 

780 """Identity operation that intercepts both forward and backward to call 

781 coordinator rendezvous, enabling deterministic comm/compute overlap. 

782 

783 This is the differentiable building block for dual-pipe schedules. 

784 In the forward pass the coordinator is invoked with the forward-side 

785 roles for ``hook_name``; in the backward pass it is invoked with the 

786 backward-side roles. The tensor value and gradient flow through 

787 unchanged. 

788 

789 Args: 

790 x: Input tensor. Returned as-is; gradients flow through. 

791 hook_name: One of ``"A"``, ``"B"``, ``"C"``, ``"D"`` identifying 

792 the position relative to MoE dispatch/combine. 

793 coordinator: A :class:`HookCoordinator` instance shared between the 

794 forward and backward threads. 

795 

796 Returns: 

797 The same tensor *x*, attached to the autograd graph so that the 

798 backward hook will fire. 

799 """ 

800 raise NotImplementedError("Platform subclasses must implement differentiable_sync_hook") 

801 

802 @staticmethod 

803 def differentiable_all_to_all_single(input_tensor, input_splits, output_splits, group): 

804 """Variable-split all-to-all single that supports gradient flow. 

805 

806 Unlike ``all_to_all_single`` (which is not differentiable), this method 

807 wraps the collective in an autograd function so gradients are correctly 

808 routed back through the reverse all-to-all in the backward pass. 

809 Intended for Expert Parallelism token dispatch / combine. 

810 

811 Args: 

812 input_tensor: Input tensor to scatter. Shape ``[sum(input_splits), *feature_dims]``. 

813 input_splits: Per-rank sizes of data sent from this rank (list of ints, 

814 length equal to ep_degree). 

815 output_splits: Per-rank sizes of data received by this rank (list of ints, 

816 length equal to ep_degree). 

817 group: Process group (ProcessGroup for torch, group name str for mindspore). 

818 

819 Returns: 

820 Output tensor of shape ``[sum(output_splits), *feature_dims]``. 

821 

822 Raises: 

823 NotImplementedError: Must be implemented by platform subclasses. 

824 """ 

825 raise NotImplementedError("Platform subclasses must implement differentiable_all_to_all_single") 

826 

827 @staticmethod 

828 def differentiable_all_to_all_single_async(input_tensor, input_splits, output_splits, group): 

829 """Async variant of :meth:`differentiable_all_to_all_single`. 

830 

831 Same semantics but launches the collective with ``async_op=True`` and 

832 only performs a stream-level ``wait`` — the host returns immediately 

833 after dispatching the kernel. Intended for dual-pipe comm/compute 

834 overlap paths where the paired COMPUTE side's rendezvous notify must 

835 fire right after kernel launch (not after the collective actually 

836 completes on device). 

837 

838 Args: 

839 input_tensor: Input tensor to scatter. Shape ``[sum(input_splits), *feature_dims]``. 

840 input_splits: Per-rank sizes of data sent from this rank. 

841 output_splits: Per-rank sizes of data received by this rank. 

842 group: Process group. 

843 

844 Returns: 

845 Output tensor of shape ``[sum(output_splits), *feature_dims]``. 

846 

847 Raises: 

848 NotImplementedError: Must be implemented by platform subclasses. 

849 """ 

850 raise NotImplementedError( 

851 "Platform subclasses must implement differentiable_all_to_all_single_async" 

852 ) 

853 

854 @staticmethod 

855 def wait_async_tensor(tensor): 

856 """Wait for an async collective tensor to become materialised. 

857 

858 Intended for use with :class:`AsyncHandle` so that callers can 

859 wait on an async all-to-all result without importing framework-specific 

860 modules directly. The call is **idempotent** — waiting on an already- 

861 completed tensor is a no-op. 

862 

863 Args: 

864 tensor: An async collective tensor (e.g. PyTorch 

865 ``AsyncCollectiveTensor``) whose values have not yet been 

866 fully written by the remote ranks. 

867 

868 Returns: 

869 The same *tensor*, now guaranteed to be fully materialised. 

870 

871 Raises: 

872 NotImplementedError: Must be implemented by platform subclasses. 

873 """ 

874 raise NotImplementedError( 

875 "Platform subclasses must implement wait_async_tensor" 

876 ) 

877 

878 @staticmethod 

879 def arange(start, end=None, step=1, dtype=None, device=None): 

880 """Create a 1-D tensor with evenly spaced values. 

881 

882 Args: 

883 start: Start of interval (inclusive). If *end* is ``None``, 

884 treated as the stop value and *start* defaults to 0. 

885 end: End of interval (exclusive). Defaults to ``None``. 

886 step: Step size. Defaults to ``1``. 

887 dtype: Data type. ``None`` uses the framework default (int64). 

888 device: Target device. 

889 

890 Returns: 

891 1-D tensor ``[start, start+step, ..., end)``. 

892 

893 Raises: 

894 NotImplementedError: Must be implemented by platform subclasses. 

895 """ 

896 raise NotImplementedError("Platform subclasses must implement arange") 

897 

898 @staticmethod 

899 def zeros(size, dtype=None, device=None): 

900 """Create a zero-filled tensor of the given shape. 

901 

902 Args: 

903 size: Shape of the tensor (a single tuple/list). 

904 dtype: Desired data type. ``None`` uses the framework default (float32). 

905 device: Target device. ``None`` uses the framework default. 

906 

907 Returns: 

908 Zero-filled tensor of the specified shape. 

909 

910 Raises: 

911 NotImplementedError: Must be implemented by platform subclasses. 

912 """ 

913 raise NotImplementedError("Platform subclasses must implement zeros") 

914 

915 @staticmethod 

916 def parameters_dict(cell): 

917 """Get the parameters dictionary of a cell/module. 

918 

919 Args: 

920 cell: The cell or module to get parameters from. 

921 

922 Returns: 

923 dict: A dictionary mapping parameter names to parameters. 

924 """ 

925 raise NotImplementedError("Platform subclasses must implement parameters_dict") 

926 

927 @staticmethod 

928 def buffers_dict(cell: Any) -> Any: 

929 """Get the named buffers of a cell/module. 

930 

931 Args: 

932 cell: The cell or module to get buffers from. 

933 

934 Returns: 

935 An iterable of ``(name, buffer)`` pairs, including non-persistent 

936 buffers and buffers registered by child modules. 

937 """ 

938 raise NotImplementedError("Platform subclasses must implement buffers_dict") 

939 

940 @staticmethod 

941 def get_model_state_dict(model: Any, *, options: Any = None) -> dict[str, Any]: 

942 """Get the state dictionary of a model. 

943 

944 Args: 

945 model: The model to extract state from. 

946 options: Optional configuration for state dict extraction. 

947 

948 Returns: 

949 dict: The state dictionary containing model parameters and buffers. 

950 

951 Raises: 

952 NotImplementedError: Platform subclasses must implement this method. 

953 """ 

954 raise NotImplementedError( 

955 "Platform subclasses must implement get_model_state_dict" 

956 ) 

957 

958 @staticmethod 

959 def set_model_state_dict(model: Any, model_state_dict: dict[str, Any], *, options: Any = None) -> None: 

960 """Set the state dictionary of a model. 

961 

962 Args: 

963 model: The model to load state into. 

964 model_state_dict: The state dict to load into the model. 

965 options: Optional configuration for state dict loading. 

966 

967 Returns: 

968 None. 

969 

970 Raises: 

971 NotImplementedError: Platform subclasses must implement this method. 

972 """ 

973 raise NotImplementedError( 

974 "Platform subclasses must implement set_model_state_dict" 

975 ) 

976 

977 @staticmethod 

978 def save_checkpoint(cell, file_path: str, ckpt_format: str = "safetensors") -> None: 

979 """Save a cell/module checkpoint to file. 

980 

981 Args: 

982 cell: The cell or module to save. 

983 file_path (str): The path to save the checkpoint to. 

984 ckpt_format (str): The file format. 

985 """ 

986 raise NotImplementedError("Platform subclasses must implement save_checkpoint") 

987 

988 @staticmethod 

989 def load_checkpoint(file_path: str, ckpt_format: str = "safetensors") -> dict: 

990 """Load a checkpoint from file. 

991 

992 Args: 

993 file_path (str): The path to load the checkpoint from. 

994 ckpt_format (str): The file format. 

995 

996 Returns: 

997 dict: The loaded checkpoint state dictionary. 

998 """ 

999 raise NotImplementedError("Platform subclasses must implement load_checkpoint") 

1000 

1001 def _create_group(self, rank_list): 

1002 """Create a new process group with the specified ranks. 

1003 

1004 Internal method to be implemented by subclasses. 

1005 

1006 Args: 

1007 rank_list (list): List of ranks to include in the group. 

1008 

1009 Returns: 

1010 The newly created process group. 

1011 """ 

1012 raise NotImplementedError("Platform subclasses must implement _create_group") 

1013 

1014 def new_stream(self): 

1015 """Create a new compute stream for asynchronous operations. 

1016 

1017 Returns: 

1018 A new stream object for the current device. 

1019 """ 

1020 raise NotImplementedError("Platform subclasses must implement new_stream") 

1021 

1022 def get_stream_context(self): 

1023 """Get a context manager for executing operations on a specific stream. 

1024 

1025 Returns: 

1026 A context manager that can be used with 'with' statement to set stream. 

1027 """ 

1028 raise NotImplementedError("Platform subclasses must implement get_stream_context") 

1029 

1030 @staticmethod 

1031 def get_tensor_transform(): 

1032 """Get the tensor transformation utilities for the current framework. 

1033 

1034 Returns: 

1035 A module or object containing tensor transformation functions. 

1036 """ 

1037 raise NotImplementedError("Platform subclasses must implement get_tensor_transform") 

1038 

1039 @staticmethod 

1040 def construct_strided_slice(x, begin, end, stride): 

1041 """Construct a strided slice operation on a tensor. 

1042 

1043 Args: 

1044 x: The input tensor to slice. 

1045 begin: The starting indices for each dimension. 

1046 end: The ending indices for each dimension. 

1047 stride: The stride for each dimension. 

1048 

1049 Returns: 

1050 The sliced tensor. 

1051 """ 

1052 raise NotImplementedError("Platform subclasses must implement construct_strided_slice") 

1053 

1054 @staticmethod 

1055 def micro_batch(micro_batch_num, args_batch_dim=None, kwargs_batch_dim=None): 

1056 """Split inputs into micro-batches for pipeline parallelism. 

1057 

1058 Args: 

1059 micro_batch_num (int): The number of micro-batches to create. 

1060 args_batch_dim (list, optional): Batch dimension for each positional arg. 

1061 kwargs_batch_dim (dict, optional): Batch dimension for each keyword arg. 

1062 

1063 Returns: 

1064 A decorator that splits function inputs into micro-batches. 

1065 """ 

1066 raise NotImplementedError("Platform subclasses must implement micro_batch") 

1067 

1068 @staticmethod 

1069 def get_symmetric_memory_handler(): 

1070 """Return a platform-specific symmetric memory handler instance.""" 

1071 raise NotImplementedError("Platform subclasses must implement get_symmetric_memory_handler") 

1072 

1073 @staticmethod 

1074 def load_into_param(param, data): 

1075 """Load data into a parameter, handling framework-specific semantics.""" 

1076 raise NotImplementedError("Platform subclasses must implement load_into_param") 

1077 

1078 def create_group(self, rank_list): 

1079 """Create or retrieve a communication group with the specified ranks. 

1080 

1081 If a group with the same rank list already exists, returns the existing 

1082 group instead of creating a new one. 

1083 

1084 Args: 

1085 rank_list (list): List of ranks to include in the group. 

1086 

1087 Returns: 

1088 The process group for the specified ranks. 

1089 """ 

1090 group_key = str(tuple(sorted(rank_list))) 

1091 if group_key in EXISTING_COMM_GROUPS: 

1092 return EXISTING_COMM_GROUPS[group_key] 

1093 

1094 group = self._create_group(rank_list) 

1095 EXISTING_COMM_GROUPS[group_key] = group 

1096 return group 

1097 

1098 @staticmethod 

1099 def _process_current_handle(): 

1100 """Wait for the current gradient handle and execute post-process callback. 

1101 

1102 Internal method to synchronize pending gradient operations. 

1103 """ 

1104 if Platform.current_grad_handle is None: 

1105 return 

1106 

1107 Platform.current_grad_handle.wait() 

1108 if Platform.post_grad_handle_process is None: 

1109 return 

1110 # pylint: disable=E1102 

1111 Platform.post_grad_handle_process() 

1112 

1113 def set_grad_reduce_handle(self, handle, post_process=None): 

1114 """Set a new gradient reduction handle after waiting for the current one. 

1115 

1116 Waits for any pending gradient handle on the grad sync stream, then 

1117 sets the new handle and optional post-process callback. 

1118 

1119 Args: 

1120 handle: The async work handle for gradient reduction. 

1121 post_process (callable, optional): Callback to run after handle completes. 

1122 """ 

1123 if Platform.grad_sync_stream is None: 

1124 Platform.grad_sync_stream = self.new_stream() 

1125 stream_context = self.get_stream_context() 

1126 with stream_context(Platform.grad_sync_stream): 

1127 Platform._process_current_handle() 

1128 Platform.current_grad_handle = handle 

1129 Platform.post_grad_handle_process = post_process 

1130 

1131 def wait_grad_handle(self): 

1132 """Wait for the current gradient handle to complete. 

1133 

1134 Blocks until the current gradient reduction handle completes and 

1135 clears the handle state. 

1136 """ 

1137 if Platform.current_grad_handle is None: 

1138 return 

1139 if Platform.grad_sync_stream is None: 

1140 Platform.grad_sync_stream = self.new_stream() 

1141 stream_context = self.get_stream_context() 

1142 with stream_context(Platform.grad_sync_stream): 

1143 Platform._process_current_handle() 

1144 sync_event = Platform.grad_sync_stream.record_event() 

1145 sync_event.wait() 

1146 Platform.current_grad_handle = None 

1147 Platform.post_grad_handle_process = None 

1148 

1149 @staticmethod 

1150 def all_gather_object(object_list, obj, group=None) -> None: 

1151 """Gather Python objects from all ranks into a list. 

1152 

1153 Each rank contributes its object, and all ranks receive the complete list. 

1154 

1155 Args: 

1156 object_list (list): List to store gathered objects (output parameter). 

1157 obj: The Python object from this rank to contribute. 

1158 group: The process group for communication. Defaults to None (default group). 

1159 """ 

1160 raise NotImplementedError("Platform subclasses must implement all_gather_object") 

1161 

1162 @staticmethod 

1163 def barrier(group=None, async_op: bool = False, device_ids=None) -> Any: 

1164 """Synchronize all processes in the given process group. 

1165 

1166 Each rank blocks until every rank in the group enters this collective (when ``async_op`` 

1167 is False), or returns an async handle that must be completed before proceeding. 

1168 

1169 Args: 

1170 group: The process group or communication group. ``None`` uses the default group. 

1171 async_op (bool): If True, returns a backend-specific async work handle. Default: False. 

1172 device_ids: Optional device id list; semantics depend on the backend. 

1173 

1174 Returns: 

1175 Async work handle when ``async_op`` is True; otherwise ``None`` (unless the rank 

1176 is not in the group, in which case the backend may return ``None``). 

1177 """ 

1178 raise NotImplementedError("Platform subclasses must implement barrier") 

1179 

1180 @staticmethod 

1181 def init_process_group( 

1182 backend: Optional[str] = None, 

1183 *, 

1184 init_method: Optional[str] = None, 

1185 timeout: Optional[timedelta] = None, 

1186 world_size: int = -1, 

1187 rank: int = -1, 

1188 store: Any = None, 

1189 pg_options: Any = None, 

1190 device_id: Any = None 

1191 ) -> None: 

1192 """ 

1193 Initialize the default distributed process group. 

1194 

1195 Args: 

1196 backend: The backend to use for distributed communication 

1197 init_method: URL specifying how to initialize the process group 

1198 timeout: Timeout for operations executed against the process group 

1199 world_size: Number of processes participating in the job 

1200 rank: Rank of the current process 

1201 store: Key/value store for exchanging connection information 

1202 pg_options: Process group options for backend-specific configurations 

1203 device_id: Specific device this process will work on 

1204 

1205 Raises: 

1206 NotImplementedError: This method must be implemented by subclasses 

1207 """ 

1208 raise NotImplementedError("Platform subclasses must implement init_process_group") 

1209 

1210 @staticmethod 

1211 def destroy_process_group(group=None) -> None: 

1212 """ 

1213 Destroy a given process group. 

1214 

1215 Args: 

1216 group: The process group to be destroyed. If None, destroys the default group. 

1217 

1218 Raises: 

1219 NotImplementedError: This method must be implemented by subclasses 

1220 """ 

1221 raise NotImplementedError("Platform subclasses must implement destroy_process_group") 

1222 

1223 @staticmethod 

1224 def get_process_group_ranks(group=None) -> list[int]: 

1225 """ 

1226 Get rank list of the given process group. 

1227 

1228 Args: 

1229 group: The process group to get ranks from. If None, uses the default group. 

1230 

1231 Returns: 

1232 List of ranks in the specified process group. 

1233 

1234 Raises: 

1235 NotImplementedError: This method must be implemented by subclasses 

1236 """ 

1237 raise NotImplementedError("Platform subclasses must implement get_process_group_ranks") 

1238 

1239 @staticmethod 

1240 def get_backend(group=None): 

1241 """ 

1242 Get the backend of the given process group. 

1243 Args: 

1244 group: The process group to get backend from. If None, uses the default group. 

1245 

1246 Returns: 

1247 The backend name of the specified process group. 

1248 

1249 Raises: 

1250 NotImplementedError: This method must be implemented by subclasses 

1251 """ 

1252 raise NotImplementedError("Platform subclasses must implement get_backend") 

1253 

1254 @staticmethod 

1255 def split_group(parent_pg: Any = None, 

1256 split_ranks: Optional[list] = None, 

1257 timeout: Optional[timedelta] = None, 

1258 pg_options: Optional[Any] = None, 

1259 group_desc: Optional[str] = None, 

1260 ) -> Any: 

1261 """Create a split group relative to the parent process group. 

1262 

1263 Args: 

1264 parent_pg: The parent process group to split from. 

1265 split_ranks (list, optional): Ranks to include in the split group. 

1266 timeout (timedelta, optional): Timeout for operations. 

1267 pg_options: Process group options for backend-specific configurations. 

1268 group_desc (str, optional): Description of the group. 

1269 

1270 Returns: 

1271 The new split process group. 

1272 """ 

1273 raise NotImplementedError("Platform subclasses must implement split_group") 

1274 

1275 @staticmethod 

1276 def get_group_local_rank(group=None) -> int: 

1277 """Get the local rank within the given process group. 

1278 

1279 Args: 

1280 group: The process group to query. If None, uses the default group. 

1281 

1282 Returns: 

1283 int: The local rank within the group. 

1284 """ 

1285 raise NotImplementedError("Platform subclasses must implement get_group_local_rank") 

1286 

1287 @staticmethod 

1288 def no_grad(): 

1289 """Get a context manager to disable gradient computation. 

1290 

1291 Returns: 

1292 A context manager that disables gradient tracking. 

1293 """ 

1294 raise NotImplementedError("Platform subclasses must implement no_grad") 

1295 

1296 @staticmethod 

1297 def preserve_version_counter(tensor): 

1298 """Get a context manager that preserves version for an internal tensor update.""" 

1299 raise NotImplementedError("Platform subclasses must implement preserve_version_counter") 

1300 

1301 @staticmethod 

1302 def relu(tensor): 

1303 """Apply ReLU activation element-wise. 

1304 

1305 Args: 

1306 tensor: Input tensor. 

1307 

1308 Returns: 

1309 Tensor with ReLU applied (max(0, x)). 

1310 """ 

1311 raise NotImplementedError("Platform subclasses must implement relu") 

1312 

1313 @staticmethod 

1314 def cat(tensors, dim=0): 

1315 """Concatenate tensors along a dimension.""" 

1316 raise NotImplementedError("Platform subclasses must implement cat") 

1317 

1318 @staticmethod 

1319 def empty_like(tensor, *, dtype=None, device=None, pin_memory=False): 

1320 """Create an uninitialized tensor with the same shape as input. 

1321 

1322 Args: 

1323 tensor: The input tensor to copy shape from. 

1324 dtype: Optional dtype for the new tensor. If None, uses input tensor's dtype. 

1325 device: Optional device for the new tensor. If None, uses input tensor's device. 

1326 pin_memory (bool): If True, allocate pinned memory for faster CPU-GPU transfer. 

1327 

1328 Returns: 

1329 An uninitialized tensor with the same shape as input. 

1330 """ 

1331 raise NotImplementedError("Platform subclasses must implement empty_like") 

1332 

1333 def get_current_stream(self): 

1334 """Get the current compute stream for the device. 

1335 

1336 Returns: 

1337 The current stream object. 

1338 """ 

1339 raise NotImplementedError("Platform subclasses must implement get_current_stream") 

1340 

1341 def new_event(self): 

1342 """Create a new event for stream synchronization. 

1343 

1344 Returns: 

1345 A new event object. 

1346 """ 

1347 raise NotImplementedError("Platform subclasses must implement new_event") 

1348 

1349 def tree_map(self, fn, tree): 

1350 """Apply a function to all tensors in a nested structure. 

1351 

1352 Args: 

1353 fn (callable): Function to apply to each tensor. 

1354 tree: Nested structure (list, tuple, dict) containing tensors. 

1355 

1356 Returns: 

1357 The same nested structure with fn applied to all tensors. 

1358 """ 

1359 raise NotImplementedError("Platform subclasses must implement tree_map") 

1360 

1361 @staticmethod 

1362 def is_linear_module(module) -> bool: 

1363 """Check whether *module* is a linear/dense layer for the current framework. 

1364 

1365 Args: 

1366 module: The module instance to check. 

1367 

1368 Returns: 

1369 True if *module* is the framework's linear layer type. 

1370 """ 

1371 raise NotImplementedError("Platform subclasses must implement is_linear_module") 

1372 

1373 @staticmethod 

1374 def is_embedding_module(module) -> bool: 

1375 """Check whether *module* is an embedding layer for the current framework. 

1376 

1377 Args: 

1378 module: The module instance to check. 

1379 

1380 Returns: 

1381 True if *module* is the framework's embedding layer type. 

1382 """ 

1383 raise NotImplementedError("Platform subclasses must implement is_embedding_module") 

1384 

1385 @staticmethod 

1386 def register_forward_pre_hook(module, hook, prepend=False, with_kwargs=False): 

1387 """Register a forward pre-hook on a module. 

1388 

1389 Args: 

1390 module: The module to register the hook on. 

1391 hook (callable): The hook function to register. 

1392 prepend (bool): If True, prepend the hook to existing hooks. 

1393 with_kwargs (bool): If True, hook receives both args and kwargs. 

1394 

1395 Returns: 

1396 A handle that can be used to remove the hook. 

1397 """ 

1398 return module.register_forward_pre_hook(hook, prepend=prepend, with_kwargs=with_kwargs) 

1399 

1400 @staticmethod 

1401 def register_full_backward_hook(module, hook, prepend=False): 

1402 """Register a full backward hook on a module. 

1403 

1404 Args: 

1405 module: The module to register the hook on. 

1406 hook (callable): The hook function to register. 

1407 prepend (bool): If True, prepend the hook to existing hooks. 

1408 

1409 Returns: 

1410 A handle that can be used to remove the hook. 

1411 """ 

1412 return module.register_full_backward_hook(hook, prepend) 

1413 

1414 @staticmethod 

1415 def register_full_backward_pre_hook(module, hook, prepend=False): 

1416 """Register a full backward pre-hook on a module. 

1417 

1418 Args: 

1419 module: The module to register the hook on. 

1420 hook (callable): The hook function to register. 

1421 prepend (bool): If True, prepend the hook to existing hooks. 

1422 

1423 Returns: 

1424 A handle that can be used to remove the hook. 

1425 """ 

1426 return module.register_full_backward_pre_hook(hook, prepend) 

1427 

1428 @property 

1429 def checkpoint(self): 

1430 """Get the checkpoint function for activation checkpointing. 

1431 

1432 Returns: 

1433 The checkpoint function for the current framework. 

1434 """ 

1435 raise NotImplementedError("Platform subclasses must implement checkpoint") 

1436 

1437 @staticmethod 

1438 def checkpoint_wrapper(module, **checkpoint_kwargs): 

1439 """Wrap a module with activation checkpointing functionality. 

1440 

1441 Args: 

1442 module: The module or callable to wrap with activation checkpointing. 

1443 **checkpoint_kwargs: Keyword arguments forwarded to the framework 

1444 checkpoint wrapper implementation. 

1445 

1446 Returns: 

1447 The wrapped module with activation checkpointing enabled. 

1448 """ 

1449 raise NotImplementedError("Platform subclasses must implement checkpoint_wrapper") 

1450 

1451 @staticmethod 

1452 def checkpoint_exclude_wrapper(module: Any) -> Any: 

1453 """Wrap a callable whose activations should be saved instead of recomputed. 

1454 

1455 Args: 

1456 module: The module or callable to exclude from activation recomputation. 

1457 

1458 Returns: 

1459 The wrapped module or callable. 

1460 """ 

1461 raise NotImplementedError("checkpoint_exclude_wrapper is currently only supported on MindSpore") 

1462 

1463 @staticmethod 

1464 def swap_wrapper(module, policy_fn=None, group_swap=False): 

1465 """Wrap a module with activation swap functionality. 

1466 

1467 Args: 

1468 module: The module to wrap with activation swap. 

1469 policy_fn: Optional per-tensor swap policy function. 

1470 group_swap (bool, optional): Whether tensors participate in group copy fusion. Default: ``False``. 

1471 

1472 Returns: 

1473 The wrapped module with activation swap enabled. 

1474 """ 

1475 raise NotImplementedError("Platform subclasses must implement swap_wrapper") 

1476 

1477 @staticmethod 

1478 def swap_tensor_wrapper(target, tag=None, group_swap=False): 

1479 """Register target tensors into the current swap group. 

1480 

1481 Args: 

1482 target: A tensor or nested container of tensors to register. 

1483 tag: Optional debug tag associated with the wrapped tensors. 

1484 group_swap (bool, optional): Whether tensors participate in group copy fusion. Default: ``False``. 

1485 

1486 Returns: 

1487 The original target structure, unchanged semantically. 

1488 """ 

1489 raise NotImplementedError("Platform subclasses must implement swap_tensor_wrapper") 

1490 

1491 @staticmethod 

1492 def get_class_activation_wrapper(): 

1493 """Return the platform-specific activation wrapper class.""" 

1494 raise NotImplementedError("Platform subclasses must implement get_class_activation_wrapper") 

1495 

1496 @property 

1497 def noop_context_fn(self): 

1498 """Get a no-op context function for checkpointing. 

1499 

1500 Returns: 

1501 A context function that performs no operation. 

1502 """ 

1503 raise NotImplementedError("Platform subclasses must implement noop_context_fn") 

1504 

1505 @staticmethod 

1506 def create_selective_checkpoint_contexts(policy_fn_or_list, allow_cache_entry_mutation=False, group_swap=False): 

1507 """Create contexts for selective activation checkpointing. 

1508 

1509 Args: 

1510 policy_fn_or_list: A policy function or list of layer names to checkpoint. 

1511 allow_cache_entry_mutation (bool): Whether to allow cache entry mutation. 

1512 group_swap (bool, optional): Whether MUST_SWAP tensors participate in group copy fusion. Default: ``False``. 

1513 

1514 Returns: 

1515 Context functions for selective checkpointing. 

1516 """ 

1517 raise NotImplementedError("Platform subclasses must implement create_selective_checkpoint_contexts") 

1518 

1519 @staticmethod 

1520 def async_save_on_cpu(policy_fn=None, group_swap: bool = False): 

1521 """Create an async CPU offload context for activation checkpointing. 

1522 

1523 Args: 

1524 policy_fn: Optional policy function to determine which activations to offload. 

1525 group_swap (bool): Whether swapped tensors participate in group copy fusion. 

1526 Default: ``False``. 

1527 

1528 Returns: 

1529 Context manager for async CPU offloading during checkpointing. 

1530 """ 

1531 raise NotImplementedError("Platform subclasses must implement async_save_on_cpu") 

1532 

1533 @staticmethod 

1534 def recompute_handle_collector_ctx(): 

1535 """Context manager that collects recompute handles created in its scope. 

1536 

1537 Yields: 

1538 A list populated with one opaque recompute handle per checkpointed 

1539 block executed during the forward pass within the context. Each 

1540 handle can later be fired via :meth:`recompute_handle`. 

1541 """ 

1542 raise NotImplementedError("Platform subclasses must implement recompute_handle_collector_ctx") 

1543 

1544 @staticmethod 

1545 def recompute_handle(handle, session_id): 

1546 """Eagerly fire one checkpointed block's forward re-run. 

1547 

1548 Materializes and caches the block's activations under ``session_id`` so 

1549 a later backward in the same session reuses them instead of re-running. 

1550 

1551 Args: 

1552 handle: An opaque recompute handle from 

1553 :meth:`recompute_handle_collector_ctx`. 

1554 session_id: Stable key shared by the producing re-run and the 

1555 consuming backward. 

1556 """ 

1557 raise NotImplementedError("Platform subclasses must implement recompute_handle") 

1558 

1559 @staticmethod 

1560 def recompute_session_ctx(session_id, retain_on_unpack=False): 

1561 """Context manager binding recompute unpack to a caller-provided session. 

1562 

1563 Args: 

1564 session_id: Stable session key. Recompute caches are keyed by this 

1565 instead of the transient autodiff engine id, so a re-run fired 

1566 under one engine can be reused by another. 

1567 retain_on_unpack (bool): When ``True``, unpack returns recomputed 

1568 tensors without popping them, so a later backward can consume 

1569 them. Default: ``False``. 

1570 

1571 Returns: 

1572 A context manager activating the session for its scope. 

1573 """ 

1574 raise NotImplementedError("Platform subclasses must implement recompute_session_ctx") 

1575 

1576 @staticmethod 

1577 def clear_recompute_session(session_id): 

1578 """Release retained recompute data for a session. 

1579 

1580 Args: 

1581 session_id: The session key whose cached recompute data is cleared. 

1582 """ 

1583 raise NotImplementedError("Platform subclasses must implement clear_recompute_session") 

1584 

1585 @staticmethod 

1586 def get_element_size(tensor): 

1587 """Get Tensor Element Size""" 

1588 raise NotImplementedError("Platform subclasses must implement get_element_size") 

1589 

1590 @staticmethod 

1591 def alloc_tensor_buffer(numel: int, dtype, device, pin_memory: bool = False): 

1592 """Allocate an uninitialized 1-D tensor buffer.""" 

1593 raise NotImplementedError("Platform subclasses must implement alloc_tensor_buffer") 

1594 

1595 @staticmethod 

1596 def tensor_to_numpy(tensor) -> np.ndarray: 

1597 """Convert a framework tensor to a NumPy array. 

1598 

1599 Args: 

1600 tensor: The tensor to convert. 

1601 

1602 Returns: 

1603 np.ndarray: The tensor data as a NumPy array. 

1604 """ 

1605 raise NotImplementedError("Platform subclasses must implement tensor_to_numpy") 

1606 

1607 @staticmethod 

1608 def from_numpy(np_array): 

1609 """Create a host-resident tensor from a NumPy array (inverse of tensor_to_numpy). 

1610 

1611 The result stays on the host regardless of the active device context, so it 

1612 remains asnumpy-able even when built under ``ms.DeviceCtx("meta")`` (e.g. while 

1613 ``fully_shard`` lazily constructs a default device mesh). Use it for rank/mesh 

1614 bookkeeping tensors, which are only ever read back via ``tensor_to_numpy``. 

1615 """ 

1616 raise NotImplementedError("Platform subclasses must implement from_numpy") 

1617 

1618 @staticmethod 

1619 def profiler_record(name): 

1620 """Record a profiler event with the given name. 

1621 

1622 Args: 

1623 name (str): The name of the profiler event. 

1624 

1625 Returns: 

1626 A context manager or decorator for profiling a code region. 

1627 """ 

1628 raise NotImplementedError("Platform subclasses must implement profiler_record") 

1629 

1630 def cast_fp_tensor(self, dtype, x): 

1631 """Cast floating-point tensor to target dtype if applicable. 

1632 

1633 Args: 

1634 dtype: The target dtype to cast to. 

1635 x: The input tensor. 

1636 

1637 Returns: 

1638 The tensor cast to target dtype, or unchanged if not floating-point. 

1639 """ 

1640 raise NotImplementedError("Platform subclasses must implement cast_fp_tensor") 

1641 

1642 def apply_to_tensors(self, fn, container): 

1643 """Recursively apply a function to all tensors in a container. 

1644 

1645 Supports nested structures including lists, tuples, and dicts. 

1646 

1647 Args: 

1648 fn (callable): Function to apply to each tensor. 

1649 container: Nested structure containing tensors. 

1650 

1651 Returns: 

1652 The same structure with fn applied to all tensors. 

1653 """ 

1654 raise NotImplementedError("Platform subclasses must implement apply_to_tensors") 

1655 

1656 @staticmethod 

1657 def clip_grad_norm_( 

1658 parameters, max_norm: float, norm_type: float = 2.0, 

1659 error_if_nonfinite: bool = False, foreach=None, 

1660 ): 

1661 """Compute and clip gradient norms for distributed models. 

1662 

1663 Communication is derived from each parameter's DTensor spec. 

1664 Subclasses must implement this method. 

1665 

1666 Args: 

1667 parameters: An ``nn.Module``, a single ``Tensor``, or an 

1668 iterable of ``Tensor`` s whose gradients to clip. 

1669 max_norm: Maximum allowed gradient norm. 

1670 norm_type: Type of the norm (default ``2.0``). 

1671 error_if_nonfinite: If ``True``, raise when total norm is 

1672 non-finite. Default ``False``. 

1673 foreach: Unused, accepted for API compatibility. 

1674 

1675 Returns: 

1676 The total (unclipped) gradient norm. 

1677 """ 

1678 raise NotImplementedError( 

1679 "Platform subclasses must implement clip_grad_norm_" 

1680 ) 

1681 

1682 @staticmethod 

1683 def get_created_group(rank_list: Union[list[int], tuple[int]]): 

1684 """Get an existing process group by rank list. 

1685 

1686 Args: 

1687 rank_list (Union[list[int], tuple[int]]): Tuple or list of ranks. 

1688 

1689 Returns: 

1690 The process group corresponding to the rank list if it exists, else None. 

1691 """ 

1692 group_key = str(tuple(sorted(rank_list))) 

1693 if group_key in EXISTING_COMM_GROUPS: 

1694 return EXISTING_COMM_GROUPS[group_key] 

1695 return None 

1696 

1697 @classmethod 

1698 def mark_created_groups(cls, process_group: Union[Any, list[Any]]) -> None: 

1699 """Register process groups in the global cache for reuse. 

1700 

1701 Args: 

1702 process_group (Union[Any, list[Any]]): A process group or a list of process groups. 

1703 """ 

1704 if not isinstance(process_group, list): 

1705 process_group = [process_group] 

1706 for group in process_group: 

1707 rank_list = cls.get_process_group_ranks(group) 

1708 group_key = str(tuple(sorted(rank_list))) 

1709 EXISTING_COMM_GROUPS[group_key] = group 

1710 

1711 @property 

1712 def meta_device(self): 

1713 """Get the framework-specific meta device for tensor shape inference. 

1714 

1715 The meta device allows creating tensors without allocating actual storage, 

1716 useful for shape inference and model initialization. 

1717 

1718 Returns: 

1719 The meta device object for the current framework. 

1720 """ 

1721 raise NotImplementedError("Platform subclasses must implement meta_device") 

1722 

1723 def init_on_device(self, device, include_buffers=False): 

1724 """Get a context manager for initializing module parameters on a device. 

1725 

1726 Args: 

1727 device: The target device for parameter initialization. 

1728 include_buffers (bool): If True, also initialize buffers on the device. 

1729 

1730 Returns: 

1731 A context manager for device-specific initialization. 

1732 """ 

1733 raise NotImplementedError("Platform subclasses must implement init_on_device") 

1734 

1735 def str_to_dtype(self, dtype_str: str) -> Any: 

1736 """ 

1737 Map a framework-style dtype string (e.g. ``torch.float32``) to the backend dtype object. 

1738 

1739 Args: 

1740 dtype_str (str): Serialized dtype identifier produced by checkpoint metadata. 

1741 

1742 Returns: 

1743 Framework dtype object (e.g. ``torch.dtype`` or MindSpore dtype). 

1744 """ 

1745 raise NotImplementedError("Platform subclasses must implement str_to_dtype") 

1746 

1747 def list_to_size(self, size_list: list[int]) -> Any: 

1748 """ 

1749 Convert a shape list from checkpoint metadata to the framework's size type (e.g. ``torch.Size``). 

1750 

1751 Args: 

1752 size_list (list[int]): Tensor global shape as a list of ints. 

1753 

1754 Returns: 

1755 Framework-specific size object. 

1756 """ 

1757 raise NotImplementedError("Platform subclasses must implement list_to_size")