code: def test_greedy_two_table(self) -> None:
      tables = [
          EmbeddingBagConfig(
              num_embeddings=100,
              embedding_dim=10,
              name="table_0",
              feature_names=["feature_0"],
          ),
          EmbeddingBagConfig(
              num_embeddings=100,
              embedding_dim=10,
              name="table_1",
              feature_names=["feature_1"],
          ),
      ]
  
      model = TestSparseNN(
          tables=tables, sparse_device=torch.device("meta")
      )
      search_space = self.enumerator.enumerate(
          module=model,
          sharders=[
              cast(
                  ModuleSharder[torch.nn.Module],
                  EmbeddingBagCollectionSharder(),
              )
          ],
      )
      self.greedy_proposer.load(search_space)
  
      # simulate first five iterations:
      output = []
      for _ in range(5):
          proposal = cast(
              List[ShardingOption], self.greedy_proposer.propose()
          )
          proposal.sort(
              key=lambda sharding_option: (
                  max(
                      [
                          shard.perf.total
                          for shard in sharding_option.shards
                      ]
                  ),
                  sharding_option.name,
              )
          )
          output.append(
              [
                  (
                      candidate.name,
                      candidate.sharding_type,
                      candidate.compute_kernel,
                  )
                  for candidate in proposal
              ]
          )
          self.greedy_proposer.feedback(partitionable=True)
  
      expected_output = [
          [
              ("table_0", "row_wise", "fused"),
              ("table_1", "row_wise", "fused"),
          ],
          [
              ("table_0", "table_row_wise", "fused"),
              ("table_1", "row_wise", "fused"),
          ],
          [
              ("table_1", "row_wise", "fused"),
              ("table_0", "data_parallel", "dense"),
          ],
          [
              ("table_1", "table_row_wise", "fused"),
              ("table_0", "data_parallel", "dense"),
          ],
          [
              ("table_0", "data_parallel", "dense"),
              ("table_1", "data_parallel", "dense"),
          ],
      ]
  
      self.assertEqual(expected_output, output)
  
      # Test threshold for early_stopping
      self.greedy_proposer._threshold = 10
      self.greedy_proposer.load(search_space)
  
      # With early stopping, after berf_perf_rating is assigned, after 10 iterations with
      # consecutive worse perf_rating, the returned proposal should be None.
      proposal = None
      for i in range(13):
          proposal = cast(
              List[ShardingOption], self.greedy_proposer.propose()
          )
          self.greedy_proposer.feedback(
              partitionable=True, perf_rating=100 + i
          )
      self.assertEqual(self.greedy_proposer._best_perf_rating, 100)
      self.assertEqual(proposal, None)
  
imports: import unittest
  from typing import cast, List, Optional
  from unittest.mock import MagicMock
  import torch
  from torchrec.distributed.embedding_types import EmbeddingComputeKernel
  from torchrec.distributed.embeddingbag import EmbeddingBagCollectionSharder
  from torchrec.distributed.planner.constants import BATCH_SIZE
  from torchrec.distributed.planner.enumerators import EmbeddingEnumerator
  from torchrec.distributed.planner.proposers import EmbeddingOffloadScaleupProposer, GreedyProposer, GridSearchProposer, proposers_to_proposals_list, UniformProposer
  from torchrec.distributed.planner.types import Enumerator, ParameterConstraints, Proposer, ShardingOption, Topology
  from torchrec.distributed.test_utils.test_model import TestSparseNN
  from torchrec.distributed.types import CacheParams, CacheStatistics, ModuleSharder, ShardingType
  from torchrec.modules.embedding_configs import EmbeddingBagConfig
label_desc: Use image node_id 2 for calling the TestProposers obj's underlying member method code with example usage: obj.test_greedy_two_table() without return types