Repository: sagemath/sage

Issue:
.longest_path(algorithm='backtrack') gives incorrect result
### Steps To Reproduce

The following code returns produces a path on 26 vertices:

```
G = DiGraph( ((x,y) for x,y in Tuples(range(10,100),2) if x!=y and y%sum(ZZ(x).digits())==0), format='list_of_edges' )
with seed(0):
    L = G.longest_path(algorithm='backtrack')
print(L.order())
```

while `G.longest_path(solver='gurobi')` produces a path on 64 vertices .

Also, `G.longest_path(algorithm='backtrack')` ignores the starting vertex when its given. For example, using `L = G.longest_path(94, algorithm='backtrack')` still produces a path starting at vertex 47.

### Expected Behavior

Using `algorithm='backtrack'` should produce a correct longest path, and obey other given parameters (such as a starting vertex).

### Actual Behavior

See above.

### Additional Information

_No response_

### Environment

```markdown
- **OS**: Ubuntu 22.04.3 LTS
- **Sage Version**: 10.2.beta5
```


### Checklist

- [X] I have searched the existing issues for a bug report that matches the one I want to file, without success.
- [X] I have read the documentation and troubleshoot guide


Discussion:
As documented, the `backtrack` algorithm is a randomized heuristic. So we cannot expect an optimal solution.
```
          * While the MILP formulation returns an exact answer, the backtrack
            algorithm is a randomized heuristic.
```
I agree that the backtrack algorithm should not ignore the starting / ending vertices, but I don't know how to change this heuristic to do that. One should propose another method.
This is related to https://ask.sagemath.org/question/69746/some-strange-behaviors-of-longest_path/
A couple of comments:

- the term "backtrack" is confusing here since typically [backtracking](https://en.wikipedia.org/wiki/Backtracking) refers to a **deterministic** algorithm that is guaranteed to find all solutions. It may be worth to rename this to "heuristic" or alike to avoid confusion if there is no such guarantee.
- if there is no easy way to fix the heuristic to support some parameters, this needs to be described in the documentation and `.longest_path()` should raise an error if unsupported combination of parameters is given to it.


Resolving patch:
diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py
--- a/src/sage/graphs/generic_graph.py
+++ b/src/sage/graphs/generic_graph.py
@@ -7885,25 +7885,34 @@ def longest_path(self, s=None, t=None, use_edge_labels=False, algorithm="MILP",
           argument is set to ``None`` by default, which means that no constraint
           is set upon the first vertex in the path.
 
+          This parameter can only be used when ``algorithm`` is ``"MILP"``.
+
         - ``t`` -- a vertex (default: ``None``); forces the destination of the
           path (the method then returns the longest path ending at ``t``). The
           argument is set to ``None`` by default, which means that no constraint
           is set upon the last vertex in the path.
 
+          This parameter can only be used when ``algorithm`` is ``"MILP"``.
+
         - ``use_edge_labels`` -- boolean (default: ``False``); whether to
           compute a path with maximum weight where the weight of an edge is
           defined by its label (a label set to ``None`` or ``{}`` being
           considered as a weight of `1`), or to compute a path with the longest
           possible number of edges (i.e., edge weights are set to 1)
 
+          This parameter can only be used when ``algorithm`` is ``"MILP"``.
+
         - ``algorithm`` -- string (default: ``"MILP"``); the algorithm to use
-          among ``"MILP"`` and ``"backtrack"``. Two remarks on this respect:
+          among ``"MILP"``, ``"backtrack"`` and ``"heuristic"``:
 
-          * While the MILP formulation returns an exact answer, the backtrack
-            algorithm is a randomized heuristic.
+          * ``"MILP"`` returns an exact answer.
 
-          * As the backtrack algorithm does not support edge weighting, setting
-            ``use_edge_labels=True`` will force the use of the MILP algorithm.
+          * ``"backtrack"`` is renamed ``"heuristic"`` (:issue:`36574`).
+
+          * ``"heuristic"`` is a randomized heuristic for finding a long path in
+            an unweighted (di)graph. This heuristic does not take into account
+            parameters ``s``, ``t`` and ``use_edge_labels``. An error is raised
+            if these parameters are set.
 
         - ``solver`` -- string (default: ``None``); specify a Mixed Integer
           Linear Programming (MILP) solver to be used. If set to ``None``, the
@@ -7948,7 +7957,7 @@ def longest_path(self, s=None, t=None, use_edge_labels=False, algorithm="MILP",
         The heuristic totally agrees::
 
             sage: g = graphs.PetersenGraph()
-            sage: p = g.longest_path(algorithm="backtrack").edges(sort=True, labels=False)
+            sage: p = g.longest_path(algorithm="heuristic").edges(sort=True, labels=False)
             sage: len(p)
             9
 
@@ -7970,13 +7979,13 @@ def longest_path(self, s=None, t=None, use_edge_labels=False, algorithm="MILP",
 
         TESTS:
 
-        The argument ``algorithm`` must be either ``'backtrack'`` or
-        ``'MILP'``::
+        The argument ``algorithm`` must be either ``'backtrack'``,
+        ``'heuristic'`` or ``'MILP'``::
 
             sage: graphs.PetersenGraph().longest_path(algorithm="abc")
             Traceback (most recent call last):
             ...
-            ValueError: algorithm must be either 'backtrack' or 'MILP'
+            ValueError: algorithm must be either 'backtrack', 'heuristic' or 'MILP'
 
         Disconnected graphs not weighted::
 
@@ -8049,13 +8058,43 @@ def longest_path(self, s=None, t=None, use_edge_labels=False, algorithm="MILP",
             sage: H = {(0, 3), (2, 0), (3, 4)}
             sage: H == {x for x in G.longest_path().edge_iterator(labels=False)}        # needs sage.numerical.mip
             True
+
+        :issue:`36574`::
+
+            sage: G = graphs.PathGraph(3)
+            sage: P = G.longest_path(algorithm='backtrack')
+            doctest:...: DeprecationWarning: algorithm 'backtrack' is deprecated.
+             Use algorithm 'heuristic' instead.
+            See https://github.com/sagemath/sage/issues/36574 for details.
+            sage: G.longest_path(algorithm='heuristic', s=0)
+            Traceback (most recent call last):
+            ...
+            ValueError: parameters s, t, and use_edge_labels can not be used in
+                        combination with algorithm 'heuristic'
+            sage: G.longest_path(algorithm='heuristic', t=2)
+            Traceback (most recent call last):
+            ...
+            ValueError: parameters s, t, and use_edge_labels can not be used in
+                        combination with algorithm 'heuristic'
+            sage: G.longest_path(algorithm='heuristic', use_edge_labels=True)
+            Traceback (most recent call last):
+            ...
+            ValueError: parameters s, t, and use_edge_labels can not be used in
+                        combination with algorithm 'heuristic'
         """
         self._scream_if_not_simple()
 
-        if use_edge_labels:
-            algorithm = "MILP"
-        if algorithm not in ("backtrack", "MILP"):
-            raise ValueError("algorithm must be either 'backtrack' or 'MILP'")
+        if algorithm not in ("backtrack", "heuristic", "MILP"):
+            raise ValueError("algorithm must be either 'backtrack', 'heuristic' or 'MILP'")
+        if algorithm == "backtrack":
+            from sage.misc.superseded import deprecation
+            deprecation(36574, "algorithm 'backtrack' is deprecated. "
+                               "Use algorithm 'heuristic' instead.")
+            algorithm = 'heuristic'
+        if algorithm == 'heuristic':
+            if s is not None or t is not None or use_edge_labels:
+                raise ValueError("parameters s, t, and use_edge_labels can not "
+                                 "be used in combination with algorithm 'heuristic'")
 
         # Quick improvement
         if not self.is_connected():
@@ -8100,8 +8139,8 @@ def longest_path(self, s=None, t=None, use_edge_labels=False, algorithm="MILP",
             from sage.graphs.graph import Graph
             return [0, Graph()] if use_edge_labels else Graph()
 
-        # Calling the backtrack heuristic if asked
-        if algorithm == "backtrack":
+        # Calling the heuristic if asked
+        if algorithm == "heuristic":
             from sage.graphs.generic_graph_pyx import find_hamiltonian as fh
             x = fh(self, find_path=True)[1]
             return self.subgraph(vertices=x, edges=list(zip(x[:-1], x[1:])))


Test patch: