Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rename the backtrack algorithm of method longest_path with deprecation #36574

Merged
merged 9 commits into from
Dec 6, 2023
69 changes: 54 additions & 15 deletions src/sage/graphs/generic_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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::

Expand Down Expand Up @@ -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():
Expand Down Expand Up @@ -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:])))
Expand Down