Repository: networkx/networkx

Issue:
treewidth algos depend on node types
The treewidth functions appear to depend on the types of nodes in the input graph.

### Current Behavior

For example, the algos fail is there is a mix of nodes with integer and string types.

### Expected Behavior

The algos should not depend on node types, only the graph structure.

### Steps to Reproduce

```python
import networkx as nx
from networkx.algorithms.approximation import treewidth
G = nx.Graph()
G.add_nodes_from([0, 'a'])
tw, td = treewidth.treewidth_min_degree(G)
or
tw, td = treewidth.treewidth_min_fill_in(G)
```

### Environment

Python version: 3.8
NetworkX version:  2.8


### Additional context

Error:
  File "<somedir>/lib/python3.8/site-packages/networkx/algorithms/approximation/treewidth.py", line 106, in __init__
    heapify(self._degreeq)
TypeError: '<' not supported between instances of 'int' and 'str'



Discussion:
Thanks for the report @harristeague , I can reproduce this as well.
The problem occurs while using `heapify`. So we are providing the object in `heapify` in a way that it allows comparing the nodes (perhaps only when ties on the heap priority/weight values occur. We could use some sort of unique identifier ahead of the node in the tuple put onto the heap.

Resolving patch:
diff --git a/networkx/algorithms/approximation/treewidth.py b/networkx/algorithms/approximation/treewidth.py
--- a/networkx/algorithms/approximation/treewidth.py
+++ b/networkx/algorithms/approximation/treewidth.py
@@ -98,22 +98,23 @@ def __init__(self, graph):
         # nodes that have to be updated in the heap before each iteration
         self._update_nodes = []
 
-        self._degreeq = []  # a heapq with 2-tuples (degree,node)
+        self._degreeq = []  # a heapq with 3-tuples (degree,unique_id,node)
+        self.count = itertools.count()
 
         # build heap with initial degrees
         for n in graph:
-            self._degreeq.append((len(graph[n]), n))
+            self._degreeq.append((len(graph[n]), next(self.count), n))
         heapify(self._degreeq)
 
     def best_node(self, graph):
         # update nodes in self._update_nodes
         for n in self._update_nodes:
             # insert changed degrees into degreeq
-            heappush(self._degreeq, (len(graph[n]), n))
+            heappush(self._degreeq, (len(graph[n]), next(self.count), n))
 
         # get the next valid (minimum degree) node
         while self._degreeq:
-            (min_degree, elim_node) = heappop(self._degreeq)
+            (min_degree, _, elim_node) = heappop(self._degreeq)
             if elim_node not in graph or len(graph[elim_node]) != min_degree:
                 # outdated entry in degreeq
                 continue
@@ -145,16 +146,15 @@ def min_fill_in_heuristic(graph):
 
     min_fill_in = sys.maxsize
 
-    # create sorted list of (degree, node)
-    degree_list = [(len(graph[node]), node) for node in graph]
-    degree_list.sort()
+    # sort nodes by degree
+    nodes_by_degree = sorted(graph, key=lambda x: len(graph[x]))
+    min_degree = len(graph[nodes_by_degree[0]])
 
-    # abort condition
-    min_degree = degree_list[0][0]
+    # abort condition (handle complete graph)
     if min_degree == len(graph) - 1:
         return None
 
-    for (_, node) in degree_list:
+    for node in nodes_by_degree:
         num_fill_in = 0
         nbrs = graph[node]
         for nbr in nbrs:


Test patch:
diff --git a/networkx/algorithms/approximation/tests/test_treewidth.py b/networkx/algorithms/approximation/tests/test_treewidth.py
--- a/networkx/algorithms/approximation/tests/test_treewidth.py
+++ b/networkx/algorithms/approximation/tests/test_treewidth.py
@@ -132,13 +132,16 @@ def test_empty_graph(self):
         _, _ = treewidth_min_degree(G)
 
     def test_two_component_graph(self):
-        """Test empty graph"""
         G = nx.Graph()
         G.add_node(1)
         G.add_node(2)
         treewidth, _ = treewidth_min_degree(G)
         assert treewidth == 0
 
+    def test_not_sortable_nodes(self):
+        G = nx.Graph([(0, "a")])
+        treewidth_min_degree(G)
+
     def test_heuristic_first_steps(self):
         """Test first steps of min_degree heuristic"""
         graph = {
@@ -237,13 +240,16 @@ def test_empty_graph(self):
         _, _ = treewidth_min_fill_in(G)
 
     def test_two_component_graph(self):
-        """Test empty graph"""
         G = nx.Graph()
         G.add_node(1)
         G.add_node(2)
         treewidth, _ = treewidth_min_fill_in(G)
         assert treewidth == 0
 
+    def test_not_sortable_nodes(self):
+        G = nx.Graph([(0, "a")])
+        treewidth_min_fill_in(G)
+
     def test_heuristic_first_steps(self):
         """Test first steps of min_fill_in heuristic"""
         graph = {