code: def get_ancestors(
      start_ops, end_ops=None, include_control_inputs=False
  ):
      """
      Get all ancestor ops of the start ops.
  
      Starting from start_ops,
      follow the computation graph backwards from consumer to input to find ancestors.
      Stop navigating the graph at end_ops.
      Include both start_ops and end_ops in the returning set of ancestor ops.
  
      Args:
          start_ops (list, set): The set of ops from which to begin traversing the graph.
          end_ops (list, set): The set of ops at which to stop traversing the graph.
          include_control_inputs: Whether or not to also consider control dependencies as edges.
  
      Returns:
          Set[Operation]: The ancestor ops
      """
  
      def get_neighbors(op):
          out = [input_tensor.op for input_tensor in op.inputs]
          if include_control_inputs:
              out.extend(op.control_inputs)
          return out
  
      return traverse(
          start_ops, end_ops=end_ops, neighbors_fn=get_neighbors
      )
  
file_path: autodist/autodist/kernel/common/utils.py
imports: from collections import deque
  from tensorflow.core.framework.attr_value_pb2 import AttrValue
  from tensorflow.python.util.compat import as_bytes
  from autodist.const import AUTODIST_REPLICA_PREFIX, COLOCATION_PREFIX
label_desc: Use image node_id 9 for calling a global function with example usage: get_ancestors(start_ops, end_ops, include_control_inputs) and returns: traverse, out