Repository: networkx/networkx

Issue:
Held-Karp ascent failures due to optimize.linprog infeasibility
The recent scipy 1.9rc release caught some failures related to our use of `optimize.linprog` in the Held-Karp implementations. For example, the following tests are failing:

```
FAILED networkx/algorithms/approximation/tests/test_traveling_salesman.py::test_held_karp_ascent
FAILED networkx/algorithms/approximation/tests/test_traveling_salesman.py::test_ascent_method_asymmetric
FAILED networkx/algorithms/approximation/tests/test_traveling_salesman.py::test_ascent_method_asymmetric_2
FAILED networkx/algorithms/approximation/tests/test_traveling_salesman.py::test_held_karp_ascent_asymmetric_3
FAILED networkx/algorithms/approximation/tests/test_traveling_salesman.py::test_asadpour_real_world
FAILED networkx/algorithms/approximation/tests/test_traveling_salesman.py::test_asadpour_real_world_path
```

The specifics related to the release are addressed elsewhere (see scipy/scipy#16466 and #5816), but taking a closer look at these failures it turns out that an underlying problem is that the results from `optimize.linprog` are actually not successful (this was true for scipy 1.8 as well, but we only got warnings instead of exceptions). In each of these cases, the returned `OptimiztionResult.success == False`. I didn't look closely at each case, but the `.message` attribute for at least one case indicates redundancies in `A_eq` and `b_eq` make the problem infeasible. I'm not sure exactly what's going on and whether this has something to do with the problem formulations or maybe some problem in how we're using `optimize.linprog`.


Discussion:
I had noticed the warnings coming from `optimize.linprog` and always wanted to go back and try to fix them. I guess scipy has forced my hand to at least try :smiley:. Unfortunately, the linear programming part of this has always been my weakest point with the algorithm. Also, I don't have access to my annotated copy of Held and Karp's paper at the moment but I should still have a PDF copy somewhere on my laptop.

My inclination is that the issue is not with the original problem formulation from the paper or with scipy. I most likely have a subtle error in populating the constraint matrices before they are passed to scipy. I will have time to start the investigative process tomorrow, but I don't really know how long it will take to track down the problem. Only that it will take a day or two for me to get back up to speed with the held karp ascent method. 

Resolving patch:
diff --git a/networkx/algorithms/approximation/traveling_salesman.py b/networkx/algorithms/approximation/traveling_salesman.py
--- a/networkx/algorithms/approximation/traveling_salesman.py
+++ b/networkx/algorithms/approximation/traveling_salesman.py
@@ -668,21 +668,15 @@ def direction_of_ascent():
             a_eq = np.empty((len(G) + 1, len(minimum_1_arborescences)), dtype=int)
             b_eq = np.zeros(len(G) + 1, dtype=int)
             b_eq[len(G)] = 1
-            arb_count = 0
-            for arborescence in minimum_1_arborescences:
+            for arb_count, arborescence in enumerate(minimum_1_arborescences):
                 n_count = len(G) - 1
                 for n, deg in arborescence.degree:
                     a_eq[n_count][arb_count] = deg - 2
                     n_count -= 1
                 a_eq[len(G)][arb_count] = 1
-                arb_count += 1
-            program_result = optimize.linprog(
-                c, A_eq=a_eq, b_eq=b_eq, method="interior-point"
-            )
-            bool_result = program_result.x >= 0
-            if program_result.status == 0 and np.sum(bool_result) == len(
-                minimum_1_arborescences
-            ):
+            program_result = optimize.linprog(c, A_eq=a_eq, b_eq=b_eq)
+            # If the constants exist, then the direction of ascent doesn't
+            if program_result.success:
                 # There is no direction of ascent
                 return None, minimum_1_arborescences
 


Test patch: