code: def _get_neighbor_artifact_pairs(
      events: List[metadata_store_pb2.Event],
  ) -> Iterable[Tuple[int, int]]:
      """Gets artifact_id pair of neighbors from the list of Events.
  
      Artifact a and b is considered neighbor if there exist events e1 and e2 s.t.
      (e1.artifact_id = a) AND (e2.artifact_id = b) AND
      (e1.execution_id = e2.execution_id)
  
      Args:
        events: A list of MLMD Events.
  
      Yields:
        Edge as a tuple (artifact_id_1, artifact_id_2).
      """
      execs_by_art = collections.defaultdict(set)
      arts_by_exec = collections.defaultdict(set)
      for event in events:
          execs_by_art[event.artifact_id].add(event.execution_id)
          arts_by_exec[event.execution_id].add(event.artifact_id)
      for a1 in execs_by_art:
          for a2 in set.union(
              *[arts_by_exec[e] for e in execs_by_art[a1]]
          ):
              if a1 < a2:  # Skip symmetric or self edge.
                  yield a1, a2
  
file_path: tfx/tfx/dsl/input_resolution/ops/group_by_lineage_op.py
imports: import collections
  from typing import List, Iterable, Tuple
  from tfx.dsl.input_resolution import resolver_op
  from tfx.orchestration.portable.input_resolution import exceptions
  from tfx.utils import typing_utils
  from ml_metadata.proto import metadata_store_pb2
label_desc: Use image node_id 1 for calling a global function with example usage: _get_neighbor_artifact_pairs(events) without return types