class_docstr: Reserves storage for model to be sharded with heuristical calculation. The storage
  reservation is comprised of dense tensor storage, KJT storage, and an extra
  percentage of total storage.
  
  Args:
      percentage (float): extra storage percent to reserve that acts as a margin of
          error beyond heuristic calculation of storage.
      parameter_multiplier (float): heuristic multiplier for total parameter storage.
      dense_tensor_estimate (Optional[int]): storage estimate for dense tensors, uses
          default heuristic estimate if not provided.
class_docstr_tok: ["Reserves","storage","for","model","to","be","sharded","with","heuristical","calculation",".","The","storage","reservation","is","comprised","of","dense","tensor","storage",",","KJT","storage",",","and","an","extra","percentage","of","total","storage",".","Args",":","percentage","(","float",")",":","extra","storage","percent","to","reserve","that","acts","as","a","margin","of","error","beyond","heuristic","calculation","of","storage",".","parameter_multiplier","(","float",")",":","heuristic","multiplier","for","total","parameter","storage",".","dense_tensor_estimate","(","Optional","[","int","]",")",":","storage","estimate","for","dense","tensors",",","uses","default","heuristic","estimate","if","not","provided","."]
code: def reserve(
      self,
      topology: Topology,
      batch_size: int,
      module: nn.Module,
      sharders: List[ModuleSharder[nn.Module]],
      constraints: Optional[Dict[str, ParameterConstraints]] = None,
  ) -> Topology:
      reserved_topology = copy.deepcopy(topology)
  
      (
          batch_inputs,
          shardable_modules,
      ) = _get_batch_inputs_and_shardable_parameters(
          module, sharders, batch_size, constraints
      )
  
      _reserve_storage_percentage(reserved_topology, self._percentage)
  
      self._dense_storage = _reserve_dense_storage(
          topology=reserved_topology,
          module=module,
          shardable_modules=shardable_modules,
          multiplier=self._parameter_multiplier,
          dense_tensor_estimate=self._dense_tensor_estimate,
      )
  
      self._kjt_storage = _reserve_kjt_storage(
          topology=reserved_topology,
          batch_size=batch_size,
          batch_inputs=batch_inputs,
          input_data_type_size=BIGINT_DTYPE,
          # 2 pipelined batches each with 10 internal copies
          multiplier=20,
      )
  
      if reserved_topology.devices[0].storage.hbm < 0:
          negative_storage_solution = (
              f"The reserved topology ({storage_repr_in_gb(reserved_topology.devices[0].storage)}) "
              "has negative available hbm storage, "
              "after taking into account of the reserved hbm percentage, "
              "the storage for dense modules, and the kjt storages. Hence "
              "it is not possible to find a valid sharding plan. "
              "\nPossible solutions:"
              "\n  1) If FSDP is used, consider switching to FixedPercentageStorageReservation, since "
              f"HeuristicalStorageReservation would not be able to calculate the "
              f"dense storage ({storage_repr_in_gb(self._dense_storage)}) correctly. "
              f"\n  2) Reduce local batch size ({batch_size}), which can help "
              f"reduce the per rank kjt storage ({storage_repr_in_gb(self._kjt_storage)}). "
              f"\n  3) Decrease the reserved hbm percentage ({self._percentage}). "
              "\n  4) Use hardware with a higher hbm cap (current hardware has "
              f"{storage_repr_in_gb(topology.devices[0].storage)} per rank). "
          )
          raise PlannerError(
              error_type=PlannerErrorType.INSUFFICIENT_STORAGE,
              message=negative_storage_solution,
          )
  
      return reserved_topology
  
imports: import copy
  import logging
  import math
  from typing import Dict, List, Optional, Set, Tuple
  from torch import nn
  from torchrec.distributed.planner.constants import BIGINT_DTYPE, POOLING_FACTOR
  from torchrec.distributed.planner.types import ParameterConstraints, PlannerError, PlannerErrorType, Storage, StorageReservation, Topology
  from torchrec.distributed.planner.utils import sharder_name, storage_repr_in_gb
  from torchrec.distributed.types import get_tensor_size_bytes, ModuleSharder
label_desc: Use image node_id 2 for calling the HeuristicalStorageReservation obj's underlying member method code with example usage: obj.reserve(topology, batch_size, module, sharders, constraints) and returns: reserved_topology