docs: def route(vertices_resources, nets, machine, constraints, placements,
            allocations={}, core_resource=Cores, radius=20):
      """Routing algorithm based on Neighbour Exploring Routing (NER).
  
      Algorithm refrence: J. Navaridas et al. SpiNNaker: Enhanced multicast
      routing, Parallel Computing (2014).
      http://dx.doi.org/10.1016/j.parco.2015.01.002
  
      This algorithm attempts to use NER to generate routing trees for all nets
      and routes around broken links using A* graph search. If the system is
      fully connected, this algorithm will always succeed though no consideration
      of congestion or routing-table usage is attempted.
  
      Parameters
      ----------
      radius : int
          Radius of area to search from each node. 20 is arbitrarily selected in
          the paper and shown to be acceptable in practice. If set to zero, this
          method is becomes longest dimension first routing.
      """
      wrap_around = machine.has_wrap_around_links()
  
      # Vertices constrained to route to a specific link. {vertex: route}
      route_to_endpoint = {}
      for constraint in constraints:
          if isinstance(constraint, RouteEndpointConstraint):
              route_to_endpoint[constraint.vertex] = constraint.route
  
      routes = {}
      for net in nets:
          # Generate routing tree (assuming a perfect machine)
          root, lookup = ner_net(placements[net.source],
                                 set(placements[sink] for sink in net.sinks),
                                 machine.width, machine.height,
                                 wrap_around, radius)
  
          # Fix routes to avoid dead chips/links
          if route_has_dead_links(root, machine):
              root, lookup = avoid_dead_links(root, machine, wrap_around)
  
          # Add the sinks in the net to the RoutingTree
          for sink in net.sinks:
              tree_node = lookup[placements[sink]]
              if sink in route_to_endpoint:
                  # Sinks with route-to-endpoint constraints must be routed
                  # in the according directions.
                  tree_node.children.append((route_to_endpoint[sink], sink))
              else:
                  cores = allocations.get(sink, {}).get(core_resource, None)
                  if cores is not None:
                      # Sinks with the core_resource resource specified must be
                      # routed to that set of cores.
                      for core in range(cores.start, cores.stop):
                          tree_node.children.append((Routes.core(core), sink))
                  else:
                      # Sinks without that resource are simply included without
                      # an associated route
                      tree_node.children.append((None, sink))
          routes[net] = root
  
      return routes