code: def _bidirectional_dijkstra(
      G,
      source,
      target,
      weight="weight",
      ignore_nodes=None,
      ignore_edges=None,
  ):
      """Dijkstra's algorithm for shortest paths using bidirectional search.
  
      This function returns the shortest path between source and target
      ignoring nodes and edges in the containers ignore_nodes and
      ignore_edges.
  
      This is a custom modification of the standard Dijkstra bidirectional
      shortest path implementation at networkx.algorithms.weighted
  
      Parameters
      ----------
      G : NetworkX graph
  
      source : node
         Starting node.
  
      target : node
         Ending node.
  
      weight: string, function, optional (default='weight')
         Edge data key or weight function corresponding to the edge weight
  
      ignore_nodes : container of nodes
         nodes to ignore, optional
  
      ignore_edges : container of edges
         edges to ignore, optional
  
      Returns
      -------
      length : number
          Shortest path length.
  
      Returns a tuple of two dictionaries keyed by node.
      The first dictionary stores distance from the source.
      The second stores the path from the source to that node.
  
      Raises
      ------
      NetworkXNoPath
          If no path exists between source and target.
  
      Notes
      -----
      Edge weight attributes must be numerical.
      Distances are calculated as sums of weighted edges traversed.
  
      In practice  bidirectional Dijkstra is much more than twice as fast as
      ordinary Dijkstra.
  
      Ordinary Dijkstra expands nodes in a sphere-like manner from the
      source. The radius of this sphere will eventually be the length
      of the shortest path. Bidirectional Dijkstra will expand nodes
      from both the source and the target, making two spheres of half
      this radius. Volume of the first sphere is pi*r*r while the
      others are 2*pi*r/2*r/2, making up half the volume.
  
      This algorithm is not guaranteed to work if edge weights
      are negative or are floating point numbers
      (overflows and roundoff errors can cause problems).
  
      See Also
      --------
      shortest_path
      shortest_path_length
      """
      if ignore_nodes and (
          source in ignore_nodes or target in ignore_nodes
      ):
          raise nx.NetworkXNoPath(
              f"No path between {source} and {target}."
          )
      if source == target:
          if source not in G:
              raise nx.NodeNotFound(f"Node {source} not in graph")
          return (0, [source])
  
      # handle either directed or undirected
      if G.is_directed():
          Gpred = G.predecessors
          Gsucc = G.successors
      else:
          Gpred = G.neighbors
          Gsucc = G.neighbors
  
      # support optional nodes filter
      if ignore_nodes:
  
          def filter_iter(nodes):
              def iterate(v):
                  for w in nodes(v):
                      if w not in ignore_nodes:
                          yield w
  
              return iterate
  
          Gpred = filter_iter(Gpred)
          Gsucc = filter_iter(Gsucc)
  
      # support optional edges filter
      if ignore_edges:
          if G.is_directed():
  
              def filter_pred_iter(pred_iter):
                  def iterate(v):
                      for w in pred_iter(v):
                          if (w, v) not in ignore_edges:
                              yield w
  
                  return iterate
  
              def filter_succ_iter(succ_iter):
                  def iterate(v):
                      for w in succ_iter(v):
                          if (v, w) not in ignore_edges:
                              yield w
  
                  return iterate
  
              Gpred = filter_pred_iter(Gpred)
              Gsucc = filter_succ_iter(Gsucc)
  
          else:
  
              def filter_iter(nodes):
                  def iterate(v):
                      for w in nodes(v):
                          if (v, w) not in ignore_edges and (
                              w,
                              v,
                          ) not in ignore_edges:
                              yield w
  
                  return iterate
  
              Gpred = filter_iter(Gpred)
              Gsucc = filter_iter(Gsucc)
  
      push = heappush
      pop = heappop
      # Init:   Forward             Backward
      dists = [{}, {}]  # dictionary of final distances
      paths = [
          {source: [source]},
          {target: [target]},
      ]  # dictionary of paths
      fringe = [[], []]  # heap of (distance, node) tuples for
      # extracting next node to expand
      seen = [{source: 0}, {target: 0}]  # dictionary of distances to
      # nodes seen
      c = count()
      # initialize fringe heap
      push(fringe[0], (0, next(c), source))
      push(fringe[1], (0, next(c), target))
      # neighs for extracting correct neighbor information
      neighs = [Gsucc, Gpred]
      # variables to hold shortest discovered path
      # finaldist = 1e30000
      finalpath = []
      dir = 1
      while fringe[0] and fringe[1]:
          # choose direction
          # dir == 0 is forward direction and dir == 1 is back
          dir = 1 - dir
          # extract closest to expand
          (dist, _, v) = pop(fringe[dir])
          if v in dists[dir]:
              # Shortest path to v has already been found
              continue
          # update distance
          dists[dir][v] = dist  # equal to seen[dir][v]
          if v in dists[1 - dir]:
              # if we have scanned v in both directions we are done
              # we have now discovered the shortest path
              return (finaldist, finalpath)
  
          wt = _weight_function(G, weight)
          for w in neighs[dir](v):
              if dir == 0:  # forward
                  minweight = wt(v, w, G.get_edge_data(v, w))
                  vwLength = dists[dir][v] + minweight
              else:  # back, must remember to change v,w->w,v
                  minweight = wt(w, v, G.get_edge_data(w, v))
                  vwLength = dists[dir][v] + minweight
  
              if w in dists[dir]:
                  if vwLength < dists[dir][w]:
                      raise ValueError(
                          "Contradictory paths found: negative weights?"
                      )
              elif w not in seen[dir] or vwLength < seen[dir][w]:
                  # relaxing
                  seen[dir][w] = vwLength
                  push(fringe[dir], (vwLength, next(c), w))
                  paths[dir][w] = paths[dir][v] + [w]
                  if w in seen[0] and w in seen[1]:
                      # see if this path is better than the already
                      # discovered shortest path
                      totaldist = seen[0][w] + seen[1][w]
                      if finalpath == [] or finaldist > totaldist:
                          finaldist = totaldist
                          revpath = paths[1][w][:]
                          revpath.reverse()
                          finalpath = paths[0][w] + revpath[1:]
      raise nx.NetworkXNoPath(f"No path between {source} and {target}.")
  
file_path: networkx/networkx/algorithms/simple_paths.py
imports: from heapq import heappop, heappush
  from itertools import count
  import networkx
  from networkx.algorithms.shortest_paths.weighted import _weight_function
  from networkx.utils import not_implemented_for, pairwise
label_desc: Use image node_id 8 for calling a global function with example usage: _bidirectional_dijkstra(G, source, target, weight, ignore_nodes, ignore_edges) and returns: int,  list, iterate, finaldist,  finalpath, iterate, iterate, iterate