code: def k_factor(G, k, matching_weight="weight"):
      """Compute a k-factor of G
  
      A k-factor of a graph is a spanning k-regular subgraph.
      A spanning k-regular subgraph of G is a subgraph that contains
      each vertex of G and a subset of the edges of G such that each
      vertex has degree k.
  
      Parameters
      ----------
      G : NetworkX graph
        Undirected graph
  
      matching_weight: string, optional (default='weight')
         Edge data key corresponding to the edge weight.
         Used for finding the max-weighted perfect matching.
         If key not found, uses 1 as weight.
  
      Returns
      -------
      G2 : NetworkX graph
          A k-factor of G
  
      Examples
      --------
      >>> G = nx.Graph([(1, 2), (2, 3), (3, 4), (4, 1)])
      >>> G2 = nx.k_factor(G, k=1)
      >>> G2.edges()
      EdgeView([(1, 2), (3, 4)])
  
      References
      ----------
      .. [1] "An algorithm for computing simple k-factors.",
         Meijer, Henk, Yurai Núñez-Rodríguez, and David Rappaport,
         Information processing letters, 2009.
      """
  
      from networkx.algorithms.matching import (
          is_perfect_matching,
          max_weight_matching,
      )
  
      class LargeKGadget:
          def __init__(self, k, degree, node, g):
              self.original = node
              self.g = g
              self.k = k
              self.degree = degree
  
              self.outer_vertices = [(node, x) for x in range(degree)]
              self.core_vertices = [
                  (node, x + degree) for x in range(degree - k)
              ]
  
          def replace_node(self):
              adj_view = self.g[self.original]
              neighbors = list(adj_view.keys())
              edge_attrs = list(adj_view.values())
              for outer, neighbor, edge_attrs in zip(
                  self.outer_vertices, neighbors, edge_attrs
              ):
                  self.g.add_edge(outer, neighbor, **edge_attrs)
              for core in self.core_vertices:
                  for outer in self.outer_vertices:
                      self.g.add_edge(core, outer)
              self.g.remove_node(self.original)
  
          def restore_node(self):
              self.g.add_node(self.original)
              for outer in self.outer_vertices:
                  adj_view = self.g[outer]
                  for neighbor, edge_attrs in list(adj_view.items()):
                      if neighbor not in self.core_vertices:
                          self.g.add_edge(
                              self.original, neighbor, **edge_attrs
                          )
                          break
              g.remove_nodes_from(self.outer_vertices)
              g.remove_nodes_from(self.core_vertices)
  
      class SmallKGadget:
          def __init__(self, k, degree, node, g):
              self.original = node
              self.k = k
              self.degree = degree
              self.g = g
  
              self.outer_vertices = [(node, x) for x in range(degree)]
              self.inner_vertices = [
                  (node, x + degree) for x in range(degree)
              ]
              self.core_vertices = [
                  (node, x + 2 * degree) for x in range(k)
              ]
  
          def replace_node(self):
              adj_view = self.g[self.original]
              for outer, inner, (neighbor, edge_attrs) in zip(
                  self.outer_vertices,
                  self.inner_vertices,
                  list(adj_view.items()),
              ):
                  self.g.add_edge(outer, inner)
                  self.g.add_edge(outer, neighbor, **edge_attrs)
              for core in self.core_vertices:
                  for inner in self.inner_vertices:
                      self.g.add_edge(core, inner)
              self.g.remove_node(self.original)
  
          def restore_node(self):
              self.g.add_node(self.original)
              for outer in self.outer_vertices:
                  adj_view = self.g[outer]
                  for neighbor, edge_attrs in adj_view.items():
                      if neighbor not in self.core_vertices:
                          self.g.add_edge(
                              self.original, neighbor, **edge_attrs
                          )
                          break
              self.g.remove_nodes_from(self.outer_vertices)
              self.g.remove_nodes_from(self.inner_vertices)
              self.g.remove_nodes_from(self.core_vertices)
  
      # Step 1
      if any(d < k for _, d in G.degree):
          raise nx.NetworkXUnfeasible(
              "Graph contains a vertex with degree less than k"
          )
      g = G.copy()
  
      # Step 2
      gadgets = []
      for node, degree in list(g.degree):
          if k < degree / 2.0:
              gadget = SmallKGadget(k, degree, node, g)
          else:
              gadget = LargeKGadget(k, degree, node, g)
          gadget.replace_node()
          gadgets.append(gadget)
  
      # Step 3
      matching = max_weight_matching(
          g, maxcardinality=True, weight=matching_weight
      )
  
      # Step 4
      if not is_perfect_matching(g, matching):
          raise nx.NetworkXUnfeasible(
              "Cannot find k-factor because no perfect matching exists"
          )
  
      for edge in g.edges():
          if (
              edge not in matching
              and (edge[1], edge[0]) not in matching
          ):
              g.remove_edge(edge[0], edge[1])
  
      for gadget in gadgets:
          gadget.restore_node()
  
      return g
  
imports: import networkx
  from networkx.utils import not_implemented_for
label_desc: Use image node_id 3 for calling a global function with example usage: k_factor(G, k, matching_weight) and returns: g