Coverage for  / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / core / distributed_checkpoint / standard_planner.py: 77%

273 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-07-06 05:41 +0800

1# Copyright 2026 Huawei Technologies Co., Ltd 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14# ============================================================================ 

15"""Standard planner implementations for checkpoint save and load.""" 

16from dataclasses import dataclass 

17import dataclasses 

18import pickle 

19from typing import Any, Optional, Union 

20 

21from hyper_parallel.core.distributed_checkpoint.metadata import ( 

22 CHUNK_INFO, 

23 Metadata, 

24 MetadataIndex, 

25 ChunkStorageMetadata, 

26 ChunkInfo, 

27 TensorStorageMetadata, 

28 TensorProperties, 

29 BytesStorageMetadata 

30) 

31from hyper_parallel.core.distributed_checkpoint.planner import ( 

32 SavePlan, 

33 SavePlanner, 

34 LoadPlan, 

35 LoadPlanner, 

36 WriteItem, 

37 WriteItemType, 

38 ReadItem, 

39 LoadItemType 

40) 

41from hyper_parallel.core.distributed_checkpoint.reshard import infer_slice_area_by_rank, infer_intersection 

42from hyper_parallel.core.distributed_checkpoint.util import ( 

43 narrow_tensor_by_index, 

44 chunk_to_area, 

45 create_chunk_list_for_tensor, 

46 remove_redundant_plans, 

47 flatten_state_dict, 

48 set_element, 

49) 

50from hyper_parallel.core.dtensor.dtensor import DTensor 

51from hyper_parallel.core.dtensor.layout import Layout 

52from hyper_parallel.platform import get_platform 

53 

54platform = get_platform() 

55Tensor = platform.Tensor 

56 

57 

58@dataclass(frozen=True) 

59class CachedSaveResult: 

60 """Cached finalized save result keyed by planner cache namespace.""" 

61 

62 final_plan: SavePlan 

63 metadata: Metadata 

64 

65 

66class StandardSavePlanner(SavePlanner): 

67 """Standard implementation of SavePlanner for distributed checkpoint saving.""" 

68 

69 _cached_save_result: dict[str, CachedSaveResult] = {} 

70 

71 def __init__( 

72 self, 

73 enable_plan_caching: bool = True, 

74 remove_redundancy: bool = True, 

75 save_to_minimum_rank: bool = False, 

76 ): 

77 self.state_dict: Optional[dict[str, Any]] = None 

78 self.is_coordinator: bool = False 

79 self.rank: int = 0 

80 self.remove_redundancy: bool = remove_redundancy 

81 self.save_to_minimum_rank: bool = save_to_minimum_rank 

82 self.flatten_state_dict: bool = True 

83 self._enable_plan_caching: bool = enable_plan_caching 

84 self._cached_plans_key: str = self.__class__.__name__ 

85 

86 def configure_planner(self, state_dict: dict[str, Any], **kwargs) -> None: 

87 """ 

88 Configure planner. 

89 

90 Args: 

91 state_dict (dict[str, Any]): The state_dict to save. 

92 **kwargs: Additional keyword arguments (e.g., is_coordinator, rank, remove_redundancy, 

93 save_to_minimum_rank). 

94 """ 

95 self.is_coordinator = kwargs.get("is_coordinator", False) 

96 self.rank = kwargs.get("rank", 0) 

97 self.remove_redundancy = kwargs.get("remove_redundancy", self.remove_redundancy) 

98 self.save_to_minimum_rank = kwargs.get("save_to_minimum_rank", self.save_to_minimum_rank) 

99 self.flatten_state_dict = kwargs.get("flatten_state_dict", True) 

100 

101 use_collectives = bool(kwargs.get("use_collectives", True)) 

102 if not use_collectives: 

103 self.remove_redundancy = False 

104 self._enable_plan_caching = False 

105 elif "enable_plan_caching" in kwargs: 

106 self._enable_plan_caching = bool(kwargs["enable_plan_caching"]) 

107 

108 if self.flatten_state_dict: 

109 state_dict, self.name_mapping = flatten_state_dict(state_dict) 

110 self.state_dict = state_dict 

111 self._cached_plans_key = self._build_cache_key(state_dict) 

112 

113 def _build_cache_key(self, state_dict: dict[str, Any]) -> str: 

114 """Build a stable cache namespace from sorted state_dict keys.""" 

115 return f"{self.__class__.__name__}:{'||'.join(state_dict.keys())}" 

116 

117 def build_local_plan(self) -> SavePlan: 

118 """ 

119 Create local save plan. 

120 

121 Returns: 

122 SavePlan: Local save plan containing WriteItems for this rank. 

123 """ 

124 if self.state_dict is None: 

125 raise RuntimeError("Planner not set up") 

126 

127 def compute_global_offsets(global_shape: tuple[int, ...], dtensor_layout: Layout) -> tuple[int, ...]: 

128 """ 

129 Compute the offsets of local tensor in global tensor based on layout. 

130 

131 Args: 

132 global_shape (tuple[int, ...]): Global shape of the tensor. 

133 dtensor_layout (Layout): Layout of the DTensor. 

134 

135 Returns: 

136 tuple[int, ...]: Tuple of offsets for each dimension. 

137 """ 

138 if dtensor_layout is None: 

139 # If layout is None, return all zeros (no sharding) 

140 return tuple(0 for _ in global_shape) 

141 

142 # Validate layout attributes 

143 if not hasattr(dtensor_layout, 'mesh_shape') or dtensor_layout.mesh_shape is None: 

144 raise ValueError("Layout must have mesh_shape attribute") 

145 if not hasattr(dtensor_layout, 'tensor_map') or dtensor_layout.tensor_map is None: 

146 raise ValueError("Layout must have tensor_map attribute") 

147 if not hasattr(dtensor_layout, 'rank_list') or dtensor_layout.rank_list is None: 

148 raise ValueError("Layout must have rank_list attribute") 

149 

150 current_rank = self.rank 

151 if current_rank not in dtensor_layout.rank_list: 

152 raise ValueError( 

153 f"Current rank {current_rank} not found in layout's rank_list {dtensor_layout.rank_list}") 

154 

155 inner_rank_id = dtensor_layout.rank_list.index(current_rank) 

156 # Calculate slice area using infer_slice_area_by_rank 

157 slice_area = infer_slice_area_by_rank( 

158 mesh_shape=dtensor_layout.mesh_shape, 

159 tensor_map=dtensor_layout.tensor_map, 

160 rank_id=inner_rank_id, 

161 full_shape=global_shape 

162 ) 

163 # Extract offsets (start values) from slice_area 

164 return tuple(start for start, _ in slice_area) 

165 

166 items = [] 

167 for fqn, obj in self.state_dict.items(): 

168 # Check if it's a DTensor 

169 if isinstance(obj, DTensor): 

170 # Create write item for DTensor 

171 local_tensor = obj.to_local() 

172 layout = obj.layout 

173 

174 # Get chunk metadata with offsets 

175 if layout: 

176 offsets = compute_global_offsets(obj.shape, layout) 

177 else: 

178 offsets = (0,) * len(local_tensor.shape) 

179 

180 sizes = local_tensor.shape 

181 chunk = ChunkStorageMetadata(offsets=offsets, sizes=sizes) 

182 # Get tensor properties 

183 dtype_str = str(local_tensor.dtype) if hasattr(local_tensor, 'dtype') else 'unknown' 

184 properties = TensorProperties(dtype=dtype_str) 

185 # Create write item for this tensor 

186 index = MetadataIndex(fqn=fqn, offset=offsets, index=None) 

187 write_item = WriteItem( 

188 index=index, 

189 type=WriteItemType.TENSOR, 

190 tensor_data={ 

191 'chunk': chunk, 

192 'properties': properties, 

193 'size': obj.shape, 

194 } 

195 ) 

196 items.append(write_item) 

197 elif isinstance(obj, Tensor): 

198 # Create write item for platform.Tensor: build single chunk with tensor's own size 

199 dtype_str = str(obj.dtype) if hasattr(obj, 'dtype') else 'unknown' 

200 properties = TensorProperties(dtype=dtype_str) 

201 # handle Tensor with shard information 

202 if hasattr(obj, CHUNK_INFO): 

203 if not isinstance(getattr(obj, CHUNK_INFO), ChunkInfo): 

204 raise ValueError("The attr CHUNK_INFO should be a ChunkInfo instance") 

205 chunk = getattr(obj, CHUNK_INFO).chunk 

206 # Single chunk covering the whole tensor (offsets=0, sizes=shape) 

207 else: 

208 chunk = ChunkStorageMetadata( 

209 offsets=(0,) * len(obj.shape), 

210 sizes=obj.shape, 

211 ) 

212 index = MetadataIndex(fqn=fqn, offset=chunk.offsets, index=None) 

213 write_item = WriteItem( 

214 index=index, 

215 type=WriteItemType.TENSOR, 

216 tensor_data={ 

217 'chunk': chunk, 

218 'properties': properties, 

219 'size': getattr(obj, CHUNK_INFO).global_shape if hasattr(obj, CHUNK_INFO) else obj.shape, 

220 } 

221 ) 

222 items.append(write_item) 

223 else: 

224 # Handle non-tensor types (bytes, etc.) 

225 index = MetadataIndex(fqn=fqn) 

226 write_item = WriteItem( 

227 index=index, 

228 type=WriteItemType.BYTE_IO, 

229 bytes_io_data=None 

230 ) 

231 items.append(write_item) 

232 

233 plan = SavePlan(items=items) 

234 if self.flatten_state_dict: 

235 plan.planner_data = self.name_mapping 

236 return plan 

237 

238 def build_global_plan(self, all_plans: list[SavePlan]) -> tuple[list[SavePlan], Metadata]: 

239 """ 

240 Build global plan from all local plans. 

241 

242 Collects chunks from all ranks, validates consistency, and creates metadata for the checkpoint. 

243 

244 Args: 

245 all_plans (list[SavePlan]): List of local plans from all ranks. 

246 

247 Returns: 

248 tuple[list[SavePlan], Metadata]: Updated plans and checkpoint metadata. 

249 """ 

250 # Deduplicate plans if redundancy removal is enabled 

251 if self.remove_redundancy and len(all_plans) > 1: 

252 all_plans = remove_redundant_plans(all_plans, save_to_minimum_rank=self.save_to_minimum_rank) 

253 

254 # Collect all write items by FQN 

255 fqn_to_chunks: dict[str, list[ChunkStorageMetadata]] = {} 

256 fqn_to_properties: dict[str, TensorProperties] = {} 

257 fqn_to_size: dict[str, tuple] = {} 

258 state_dict_metadata: dict[str, Union[TensorStorageMetadata, BytesStorageMetadata]] = {} 

259 

260 final_global_plans: list[SavePlan] = [] 

261 for plan in all_plans: 

262 with_index_items = [] 

263 for item in plan.items: 

264 if item.type == WriteItemType.TENSOR and item.tensor_data: 

265 fqn = item.index.fqn 

266 chunk = item.tensor_data['chunk'] 

267 properties = item.tensor_data['properties'] 

268 size = item.tensor_data['size'] 

269 

270 # Validate consistency across ranks 

271 if fqn in fqn_to_chunks and (fqn_to_properties[fqn] != properties or fqn_to_size[fqn] != size): 

272 raise ValueError(f"The {fqn} in different rank has different properties and size.") 

273 

274 # Initialize FQN entry if not exists 

275 if fqn not in fqn_to_chunks: 

276 fqn_to_properties[fqn] = properties 

277 fqn_to_size[fqn] = size 

278 fqn_to_chunks[fqn] = [] 

279 

280 # Append chunk and set index (platform.Tensor has exactly one chunk) 

281 new_index = dataclasses.replace(item.index, index=len(fqn_to_chunks[fqn])) 

282 with_index_item = dataclasses.replace(item, index=new_index) 

283 with_index_items.append(with_index_item) 

284 fqn_to_chunks[fqn].append(chunk) 

285 

286 elif item.type == WriteItemType.BYTE_IO: 

287 with_index_items.append(item) 

288 state_dict_metadata[item.index.fqn] = BytesStorageMetadata() 

289 else: 

290 raise ValueError(f"Unsupported write item type: {item.type}") 

291 

292 final_global_plans.append(dataclasses.replace(plan, items=with_index_items)) 

293 

294 # Create metadata for all tensors 

295 for fqn, chunks in fqn_to_chunks.items(): 

296 state_dict_metadata[fqn] = TensorStorageMetadata( 

297 properties=fqn_to_properties[fqn], 

298 size=fqn_to_size[fqn], 

299 chunks=chunks 

300 ) 

301 

302 metadata = Metadata(state_dict_metadata=state_dict_metadata) 

303 if self.flatten_state_dict: 

304 merged_mapping = {} 

305 for p in all_plans: 

306 merged_mapping.update(p.planner_data) 

307 metadata.planner_data = merged_mapping 

308 return final_global_plans, metadata 

309 

310 def finalize_plan(self, plan: SavePlan) -> SavePlan: 

311 """ 

312 Finalize the plan. 

313 

314 Args: 

315 plan (SavePlan): Plan to finalize. 

316 

317 Returns: 

318 SavePlan: Finalized plan. 

319 """ 

320 return plan 

321 

322 def get_cached(self) -> Optional[CachedSaveResult]: 

323 """Return cached finalized plan and metadata when plan caching is enabled.""" 

324 if ( 

325 not self._enable_plan_caching 

326 or self._cached_plans_key not in StandardSavePlanner._cached_save_result 

327 ): 

328 return None 

329 return StandardSavePlanner._cached_save_result[self._cached_plans_key] 

330 

331 def cache_result(self, final_plan: SavePlan, metadata: Metadata) -> None: 

332 """Store finalized plan and metadata in the class-level planner cache.""" 

333 if not self._enable_plan_caching: 

334 return 

335 StandardSavePlanner._cached_save_result[self._cached_plans_key] = CachedSaveResult( 

336 final_plan=final_plan, 

337 metadata=metadata, 

338 ) 

339 

340 def get_data(self, item: WriteItem) -> Any: 

341 """ 

342 Get current runtime data from state_dict for a write item. 

343 

344 Args: 

345 item (WriteItem): Write item describing what to write. 

346 

347 Returns: 

348 Any: Runtime object to be written. 

349 """ 

350 if self.state_dict is None: 

351 raise RuntimeError("Planner not set up") 

352 fqn = item.index.fqn 

353 if fqn not in self.state_dict: 

354 raise KeyError(f"Key {fqn} not found in state_dict") 

355 obj = self.state_dict[fqn] 

356 if item.type == WriteItemType.TENSOR: 

357 if isinstance(obj, DTensor): 

358 return obj.to_local().detach().cpu() 

359 if isinstance(obj, Tensor): 

360 return obj.detach().cpu() 

361 raise TypeError(f"Write item {fqn} expected tensor-like object, got {type(obj)}") 

362 if item.type == WriteItemType.BYTE_IO: 

363 return obj 

364 raise TypeError(f"Unsupported write item type: {item.type}") 

365 

366 

367def create_read_items_for_chunk_list( 

368 fqn: str, 

369 checkpoint_md: TensorStorageMetadata, 

370 local_chunks: list[ChunkStorageMetadata], 

371) -> list[ReadItem]: 

372 """ 

373 Create ReadItems by matching local chunks (what this rank needs) with 

374 saved chunks (checkpoint_md.chunks), including resharding overlaps. 

375 

376 Mirrors torch create_read_items_for_chunk_list behavior. 

377 

378 Args: 

379 fqn (str): Fully qualified name of the tensor. 

380 checkpoint_md (TensorStorageMetadata): Tensor storage metadata from checkpoint. 

381 local_chunks (list[ChunkStorageMetadata]): List of local chunks needed by this rank. 

382 

383 Returns: 

384 list[ReadItem]: List of ReadItems for loading the required data. 

385 """ 

386 read_items: list[ReadItem] = [] 

387 saved_chunks = checkpoint_md.chunks 

388 if not local_chunks or not saved_chunks: 

389 return read_items 

390 

391 for local_idx, local_chunk in enumerate(local_chunks): 

392 local_area = chunk_to_area(local_chunk) 

393 for storage_idx, storage_chunk in enumerate(saved_chunks): 

394 saved_area = chunk_to_area(storage_chunk) 

395 overlap = infer_intersection(local_area, saved_area) 

396 if overlap is None: 

397 continue 

398 

399 dest_offsets = tuple(overlap[i][0] - local_chunk.offsets[i] for i in range(len(overlap))) 

400 storage_offsets = tuple(overlap[i][0] - storage_chunk.offsets[i] for i in range(len(overlap))) 

401 lengths = tuple(overlap[i][1] - overlap[i][0] for i in range(len(overlap))) 

402 

403 read_items.append( 

404 ReadItem( 

405 type=LoadItemType.TENSOR, 

406 dest_index=MetadataIndex(fqn=fqn, offset=local_chunk.offsets, index=local_idx), 

407 dest_offsets=dest_offsets, 

408 storage_index=MetadataIndex(fqn=fqn, offset=storage_chunk.offsets, index=storage_idx), 

409 storage_offsets=storage_offsets, 

410 lengths=lengths, 

411 ) 

412 ) 

413 return read_items 

414 

415 

416class StandardLoadPlanner(LoadPlanner): 

417 """ 

418 Standard implementation of LoadPlanner. 

419 

420 Iterate state_dict and creates load plans via chunk list for resharding support. 

421 """ 

422 

423 def __init__(self, allow_partial_load: bool = False): 

424 """ 

425 Args: 

426 allow_partial_load (bool): If True, allow loading when checkpoint has fewer keys than state_dict. 

427 Default False. 

428 """ 

429 self.state_dict: Optional[dict[str, Any]] = None 

430 self.metadata: Optional[Metadata] = None 

431 self.is_coordinator: bool = False 

432 self.rank: int = 0 

433 self.allow_partial_load = allow_partial_load 

434 self.flatten_state_dict: bool = True 

435 

436 def configure_planner(self, state_dict: dict[str, Any], metadata: Metadata, **kwargs) -> None: 

437 """ 

438 Configure planner with state dict and metadata. 

439 

440 Args: 

441 state_dict (dict[str, Any]): The state_dict to load into (modified in-place). 

442 metadata (Metadata): Checkpoint metadata. 

443 **kwargs: Additional keyword arguments (e.g., is_coordinator, rank). 

444 """ 

445 self.state_dict = state_dict 

446 self.metadata = metadata 

447 self.is_coordinator = kwargs.get("is_coordinator", False) 

448 self.rank = kwargs.get("rank", 0) 

449 self.flatten_state_dict = kwargs.get("flatten_state_dict", True) 

450 self.original_state_dict = state_dict 

451 if self.flatten_state_dict: 

452 state_dict, self.name_mapping = flatten_state_dict(state_dict) 

453 self.state_dict = state_dict 

454 

455 def build_local_plan(self) -> LoadPlan: 

456 """ 

457 Build local load plan. 

458 

459 Iterate state_dict and creates load plans via chunk list for resharding support. 

460 

461 Returns: 

462 LoadPlan: Local load plan containing ReadItems for this rank. 

463 """ 

464 if self.state_dict is None or self.metadata is None: 

465 raise RuntimeError("Planner not configured") 

466 

467 requests: list[ReadItem] = [] 

468 strict = not self.allow_partial_load 

469 for fqn, obj in self.state_dict.items(): 

470 if fqn not in self.metadata.state_dict_metadata: 

471 if fqn.endswith(('matched_adamw_rms', 'step')): 

472 continue 

473 if strict: 

474 raise RuntimeError(f"Missing key in checkpoint state_dict: {fqn}.") 

475 continue 

476 md = self.metadata.state_dict_metadata[fqn] 

477 if isinstance(md, TensorStorageMetadata): 

478 obj_size = getattr(obj, CHUNK_INFO).global_shape if hasattr(obj, CHUNK_INFO) \ 

479 else getattr(obj, "shape", None) 

480 if obj_size is None or md.size != tuple(obj_size): 

481 raise ValueError( 

482 f"Size mismatch between saved {md.size} and current: {obj_size} for {fqn}", 

483 ) 

484 if isinstance(obj, DTensor): 

485 layout = getattr(obj, "layout", None) 

486 rank_list = getattr(layout, "rank_list", None) if layout else None 

487 if rank_list is None and layout is not None: 

488 rank_list = getattr(layout, "_rank_list", None) 

489 if layout is not None and rank_list is not None: 

490 if get_platform().get_rank() not in rank_list: 

491 continue 

492 # Both DTensor and platform.Tensor: create local chunks and read items 

493 local_chunks = create_chunk_list_for_tensor(obj) 

494 requests += create_read_items_for_chunk_list(fqn, md, local_chunks) 

495 else: 

496 requests.append( 

497 ReadItem( 

498 type=LoadItemType.BYTE_IO, 

499 dest_index=MetadataIndex(fqn=fqn), 

500 dest_offsets=(0,), 

501 storage_index=MetadataIndex(fqn=fqn), 

502 storage_offsets=(0,), 

503 lengths=(0,), 

504 ) 

505 ) 

506 return LoadPlan(items=requests) 

507 

508 def build_global_plan(self, all_plans: list[LoadPlan]) -> list[LoadPlan]: 

509 """ 

510 Build global plan from all local plans. 

511 

512 For now, returns plans as-is. In a more sophisticated implementation, you might need to coordinate across ranks. 

513 

514 Args: 

515 all_plans (list[LoadPlan]): List of local plans from all ranks. 

516 

517 Returns: 

518 list[LoadPlan]: Global plans (currently returns plans as-is). 

519 """ 

520 return all_plans 

521 

522 def finalize_plan(self, plan: LoadPlan) -> LoadPlan: 

523 """ 

524 Finalize the plan (no-op for default implementation). 

525 

526 Args: 

527 plan (LoadPlan): Plan to finalize. 

528 

529 Returns: 

530 LoadPlan: Finalized plan. 

531 """ 

532 return plan 

533 

534 def acquire_tensor(self, read_item: ReadItem) -> Any: 

535 """ 

536 Acquire the destination slice (narrow view) for this read_item. 

537 

538 StorageReader uses this to copy loaded data into the correct region. 

539 Torch-aligned behavior. 

540 

541 Args: 

542 read_item (ReadItem): The read item specifying what to load. 

543 

544 Returns: 

545 Any: The destination tensor slice where data should be written 

546 (tensor-like object). 

547 """ 

548 if self.state_dict is None: 

549 raise RuntimeError("Planner not configured") 

550 

551 fqn = read_item.dest_index.fqn 

552 if fqn not in self.state_dict: 

553 raise KeyError(f"Key {fqn} not found in state_dict") 

554 

555 target = self.state_dict[fqn] 

556 local_tensor = target.to_local().detach() if isinstance(target, DTensor) else target.detach() 

557 return narrow_tensor_by_index( 

558 local_tensor, 

559 read_item.dest_offsets, 

560 read_item.lengths, 

561 ) 

562 

563 def apply_tensor(self, read_item: ReadItem, tensor: Any) -> None: 

564 """ 

565 Apply tensor after reading. 

566 

567 After read_data copies into the slice, this is no-op when tensor is the 

568 same slice. When the backend has no copy_ (e.g. mindspore), read_data 

569 passes the loaded slice here; we copy it into the destination slice. 

570 

571 Args: 

572 read_item (ReadItem): The read item that was processed. 

573 tensor (Any): The tensor data to apply (tensor-like object). 

574 """ 

575 if tensor is None: 

576 return 

577 dest_slice = self.acquire_tensor(read_item) 

578 if dest_slice is tensor: 

579 return 

580 if hasattr(dest_slice, "copy_"): 

581 dest_slice.copy_(tensor) 

582 else: 

583 # Fallback: assign into state_dict if supported 

584 dest_slice[...] = tensor 

585 

586 def apply_bytes(self, read_item: ReadItem, value: bytes) -> None: 

587 """ 

588 Load bytes data into state_dict. 

589 

590 Args: 

591 read_item (ReadItem): The read item specifying the destination. 

592 value (bytes): The bytes data to deserialize and load. 

593 """ 

594 if self.state_dict is None: 

595 raise RuntimeError("Planner not set up") 

596 

597 fqn = read_item.dest_index.fqn 

598 # Deserialize bytes 

599 obj = pickle.loads(value) 

600 self.state_dict[fqn] = obj 

601 if self.flatten_state_dict: 

602 set_element(self.original_state_dict, self.name_mapping[fqn], obj) 

603 

604 

605 

606class _DcpMergeLoadPlanner(StandardLoadPlanner): 

607 """Load planner that builds distributed checkpoint from dcp into fully ``state_dict`` (in-place).""" 

608 

609 def __init__(self) -> None: 

610 super().__init__() 

611 

612 def configure_planner(self, state_dict: dict[str, Any], metadata: Metadata, **kwargs) -> None: 

613 if len(state_dict) > 0: 

614 raise ValueError( 

615 "state_dict must be empty for _DcpMergeLoadPlanner; " 

616 "it is populated in-place from checkpoint metadata." 

617 ) 

618 

619 if metadata is None: 

620 raise ValueError("metadata must not be None for _DcpMergeLoadPlanner.") 

621 

622 self.is_coordinator = kwargs.get("is_coordinator", False) 

623 for k, v in metadata.state_dict_metadata.items(): 

624 if isinstance(v, TensorStorageMetadata): 

625 v = platform.empty( 

626 platform.list_to_size(v.size), 

627 dtype=platform.str_to_dtype(v.properties.dtype), 

628 ) 

629 

630 state_dict[k] = v 

631 if metadata.planner_data is not None and k in metadata.planner_data: 

632 set_element(state_dict, metadata.planner_data[k], v) 

633 

634 super().configure_planner( 

635 state_dict, 

636 metadata, 

637 is_coordinator=self.is_coordinator, 

638 flatten_state_dict=True, 

639 )