Support multiple edges in the MILP spanning tree packing, and fix k=1 on digraphs - #42633
Support multiple edges in the MILP spanning tree packing, and fix k=1 on digraphs#42633ProfTR55 wants to merge 3 commits into
Conversation
edge_disjoint_spanning_trees short-circuits k=1 to min_spanning_tree, which ignores the orientation of the edges. On a digraph it therefore returned a tree that need not be an arborescence rooted at the requested root, unlike the MILP formulation used for larger k: on the complete digraph of order 4 the returned graph was an in-arborescence, with the root having in-degree 3. Keep the shortcut for undirected graphs only; on a digraph the MILP formulation already handles k=1 correctly.
The formulation indexed its variables by the pair (u, v), so parallel edges collapsed into a single variable and could not be told apart. Give every edge a distinct identifier and index the variables by the resulting arcs, an undirected edge with identifier i yielding the arcs (u, v, i) and (v, u, i). Loops are discarded, as they cannot belong to a spanning tree. The subtour elimination constraints need the variable of a pair (u, v). A tree uses at most one of the parallel arcs from u to v, so the sum of their variables has the same meaning and is used instead, both in the Miller-Tucker-Zemlin and in the Desrosiers-Langevin constraints. The input check is relaxed accordingly: it now rejects loops and multiple edges only for the Roskind-Tarjan algorithm.
Mention in the description of the MILP algorithm that loops and multiple edges are supported, and update the example accordingly.
|
Documentation preview for this PR (built with commit f2f85e3; changes) is ready! 🎉 |
dcoudert
left a comment
There was a problem hiding this comment.
just a few comments. Otherwise the formulation looks good to me.
| root = next(G.vertex_iterator()) | ||
|
|
||
| if k == 1: | ||
| if k == 1 and not G.is_directed(): |
There was a problem hiding this comment.
When T = next(G.out_branchings(root).
By default the iterator searches for spanning out-branching from root, and so will raise an error if none can be found.
So call T = next(G.out_branchings(root, spanning=False) and then check if
| Only available for directed graphs; digraphs with loops and | ||
| multiple edges are supported. | ||
|
|
||
| * ``None`` -- use ``'Roskind-Tarjan'`` for undirected graphs and |
There was a problem hiding this comment.
should be updated to make Gabow default for directed graphs.
| self._scream_if_not_simple() | ||
| else: | ||
| # the Gabow and MILP backends support loops and multiple edges | ||
| self._scream_if_not_simple(allow_loops=True, allow_multiple_edges=True) |
There was a problem hiding this comment.
this test is useless. It will always pass.
| return [DiGraph(E) if G.is_directed() else Graph(E)] | ||
| return [Graph(E)] | ||
|
|
||
| D = G if G.is_directed() else DiGraph(G) |
There was a problem hiding this comment.
We can avoid the creation of D. It is now only used for the BFS strengthening constraints and I think that we can use G directly.
📚 Description
Follow-up to #42570, which added multigraph support to the Gabow backend.
This does the same for the MILP formulation of
edge_disjoint_spanning_trees,as discussed with @dcoudert, and fixes a related bug found on the way.
Multiple edges in the MILP formulation. The formulation indexed its
variables by the pair
(u, v), so parallel edges collapsed into a singlevariable. Every edge now gets a distinct identifier and the variables are
indexed by the resulting arcs, an undirected edge with identifier
iyieldingthe arcs
(u, v, i)and(v, u, i). Loops are discarded, as they cannotbelong to a spanning tree.
The subtour elimination constraints need the variable of a pair
(u, v). Atree uses at most one of the parallel arcs from
utov, so the sum of theirvariables carries exactly that meaning and is used instead, in both the
Miller-Tucker-Zemlin and the Desrosiers-Langevin constraints. The input check
now rejects loops and multiple edges only for the Roskind-Tarjan algorithm.
Bug fix:
k=1on a digraph.edge_disjoint_spanning_treesshort-circuitsk=1tomin_spanning_tree, which ignores the orientation of the edges. On adigraph it therefore returned a tree that need not be an arborescence rooted at
the requested root, unlike the MILP formulation used for larger
k. Ondigraphs.Complete(4)the returned graph was an in-arborescence, the roothaving in-degree 3. The shortcut is now used for undirected graphs only.
Validation. Beyond the doctests: complete digraphs
K_2toK_5(regression), doubled digraphs and doubled complete graphs with known packing
sizes, random multigraphs where the MILP and the Gabow backend both return a
valid packing of size
ec, undirected multigraphs, and loops being ignored.Edge-disjointness is checked with multiplicity, that is every
(u, v)is usedby at most as many trees as it has parallel copies.
📝 Checklist