class_docstr: Generates embedding sharding options for given `nn.Module`, considering user provided
  constraints.
  
  Args:
      topology (Topology): device topology.
      batch_size (int): batch size.
      constraints (Optional[Dict[str, ParameterConstraints]]): dict of parameter names
          to provided ParameterConstraints.
class_docstr_tok: ["Generates","embedding","sharding","options","for","given","`","nn.Module","`",",","considering","user","provided","constraints",".","Args",":","topology","(","Topology",")",":","device","topology",".","batch_size","(","int",")",":","batch","size",".","constraints","(","Optional","[","Dict","[","str",",","ParameterConstraints","]","]",")",":","dict","of","parameter","names","to","provided","ParameterConstraints","."]
docstr: Generates relevant sharding options given module and sharders.
  
  Args:
      module (nn.Module): module to be sharded.
      sharders (List[ModuleSharder[nn.Module]]): provided sharders for module.
  
  Returns:
      List[ShardingOption]: valid sharding options with values populated.
docstr_tok: ["Generates","relevant","sharding","options","given","module","and","sharders",".","Args",":","module","(","nn.Module",")",":","module","to","be","sharded",".","sharders","(","List","[","ModuleSharder","[","nn.Module","]","]",")",":","provided","sharders","for","module",".","Returns",":","List","[","ShardingOption","]",":","valid","sharding","options","with","values","populated","."]
code: def enumerate(
      self,
      module: nn.Module,
      sharders: List[ModuleSharder[nn.Module]],
  ) -> List[ShardingOption]:
      """
      Generates relevant sharding options given module and sharders.
  
      Args:
          module (nn.Module): module to be sharded.
          sharders (List[ModuleSharder[nn.Module]]): provided sharders for module.
  
      Returns:
          List[ShardingOption]: valid sharding options with values populated.
      """
  
      self._sharder_map = {
          sharder_name(sharder.module_type): sharder
          for sharder in sharders
      }
      sharding_options: List[ShardingOption] = []
  
      named_modules_queue = [("", module)]
      while named_modules_queue:
          child_path, child_module = named_modules_queue.pop()
          sharder_key = sharder_name(type(child_module))
          sharder = self._sharder_map.get(sharder_key, None)
          if not sharder:
              for n, m in child_module.named_children():
                  if child_path != "":
                      named_modules_queue.append(
                          (child_path + "." + n, m)
                      )
                  else:
                      named_modules_queue.append((n, m))
              continue
  
          # Determine the pooling state for all sharding_options using this
          # (child_module, child_path). With this optimization, we change enumerate()
          # from being O(N^2) with respect to the number of tables to O(N). The
          # previous quadratic behavior is because in populate_estimates() invoked below, each
          # sharding_option needs to determine its pooling state, which is does via
          # an expensive O(N) walk through the list of embedding tables. With this
          # change sharding_option.is_pooled becomes O(1).
          is_pooled = ShardingOption.module_pooled(
              child_module, child_path
          )
  
          for name, param in sharder.shardable_parameters(
              child_module
          ).items():
              (
                  input_lengths,
                  col_wise_shard_dim,
                  cache_params,
                  enforce_hbm,
                  stochastic_rounding,
                  bounds_check_mode,
              ) = _extract_constraints_for_param(
                  self._constraints, name
              )
  
              for sharding_type in self._filter_sharding_types(
                  name, sharder.sharding_types(self._compute_device)
              ):
                  for compute_kernel in self._filter_compute_kernels(
                      name,
                      sharder.compute_kernels(
                          sharding_type, self._compute_device
                      ),
                  ):
                      (
                          shard_sizes,
                          shard_offsets,
                      ) = calculate_shard_sizes_and_offsets(
                          tensor=param,
                          world_size=self._world_size,
                          local_world_size=self._local_world_size,
                          sharding_type=sharding_type,
                          col_wise_shard_dim=col_wise_shard_dim,
                      )
                      dependency = None
                      if isinstance(child_module, EmbeddingTower):
                          dependency = child_path
                      elif isinstance(
                          child_module, EmbeddingTowerCollection
                      ):
                          tower_index = _get_tower_index(
                              name, child_module
                          )
                          dependency = (
                              child_path + ".tower_" + str(tower_index)
                          )
                      sharding_options.append(
                          ShardingOption(
                              name=name,
                              tensor=param,
                              module=(child_path, child_module),
                              input_lengths=input_lengths,
                              batch_size=self._batch_size,
                              compute_kernel=compute_kernel,
                              sharding_type=sharding_type,
                              partition_by=get_partition_by_type(
                                  sharding_type
                              ),
                              shards=[
                                  Shard(size=size, offset=offset)
                                  for size, offset in zip(
                                      shard_sizes, shard_offsets
                                  )
                              ],
                              cache_params=cache_params,
                              enforce_hbm=enforce_hbm,
                              stochastic_rounding=stochastic_rounding,
                              bounds_check_mode=bounds_check_mode,
                              dependency=dependency,
                              is_pooled=is_pooled,
                          )
                      )
              if not sharding_options:
                  raise RuntimeError(
                      "No available sharding type and compute kernel combination "
                      f"after applying user provided constraints for {name}"
                  )
  
      self.populate_estimates(sharding_options)
  
      return sharding_options
  
imports: import logging
  from typing import Dict, List, Optional, Tuple, Union
  from torch import nn
  from torchrec.distributed.embedding_types import EmbeddingComputeKernel
  from torchrec.distributed.planner.constants import POOLING_FACTOR
  from torchrec.distributed.planner.shard_estimators import EmbeddingPerfEstimator, EmbeddingStorageEstimator
  from torchrec.distributed.planner.types import Enumerator, ParameterConstraints, PartitionByType, Shard, ShardEstimator, ShardingOption, Topology
  from torchrec.distributed.planner.utils import sharder_name
  from torchrec.distributed.sharding_plan import calculate_shard_sizes_and_offsets
  from torchrec.distributed.types import BoundsCheckMode, CacheParams, ModuleSharder, ShardingType
  from torchrec.modules.embedding_tower import EmbeddingTower, EmbeddingTowerCollection
label_desc: Use image node_id 2 for calling the EmbeddingEnumerator obj's underlying member method code with example usage: obj.enumerate(module, sharders) and returns: sharding_options