Skip to content

Commit daf6d6f

Browse files
Add DigraphMaximumMatching (#294)
* Add maximal mathing and maximum mathing methods and helper methods. * Add documentation and examples for DigraphMaximalMatching and DigraphMaximumMatching as well as documentation for IsMaximumMatching. * Fix typo, add examples and implementation for IsMaximumMatching * Fix manual examples * Add tests for matching methods, fix label bug in maximum matching code * Add more tests for maximum matching, improve documentation and code * Update oper.xml Remove duplicate "that" * Remove non-deterministic tests * Fix tests Co-authored-by: James Mitchell <[email protected]>
1 parent 858e113 commit daf6d6f

File tree

9 files changed

+553
-9
lines changed

9 files changed

+553
-9
lines changed

doc/attr.xml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2179,3 +2179,67 @@ true
21792179
</Description>
21802180
</ManSection>
21812181
<#/GAPDoc>
2182+
2183+
<#GAPDoc Label="DigraphMaximalMatching">
2184+
<ManSection>
2185+
<Attr Name="DigraphMaximalMatching" Arg="digraph"/>
2186+
<Returns>A list of pairs of vertices.</Returns>
2187+
<Description>
2188+
This function returns a maximal matching of the digraph <A>digraph</A>.
2189+
<P/>
2190+
2191+
For the definition of a maximal matching, see <Ref Oper="IsMaximalMatching"/>.
2192+
2193+
<Example><![CDATA[
2194+
gap> D := DigraphFromDiSparse6String(".IeAoXCJU@|SHAe?d");
2195+
<immutable digraph with 10 vertices, 13 edges>
2196+
gap> M := DigraphMaximalMatching(D);; IsMaximalMatching(D, M);
2197+
true
2198+
gap> D := RandomDigraph(100);;
2199+
gap> IsMaximalMatching(D, DigraphMaximalMatching(D));
2200+
true
2201+
gap> D := GeneralisedPetersenGraph(IsMutableDigraph, 9, 2);
2202+
<mutable digraph with 18 vertices, 54 edges>
2203+
gap> IsMaximalMatching(D, DigraphMaximalMatching(D));
2204+
true
2205+
]]></Example>
2206+
</Description>
2207+
</ManSection>
2208+
<#/GAPDoc>
2209+
2210+
<#GAPDoc Label="DigraphMaximumMatching">
2211+
<ManSection>
2212+
<Attr Name="DigraphMaximumMatching" Arg="digraph"/>
2213+
<Returns>A list of pairs of vertices.</Returns>
2214+
<Description>
2215+
This function returns a maximum matching of the digraph <A>digraph</A>.
2216+
<P/>
2217+
2218+
For the definition of a maximum matching, see <Ref Oper="IsMaximumMatching"/>.
2219+
If <A>digraph</A> is bipartite (see <Ref Oper="IsBipartiteDigraph"/>), then
2220+
the algorithm used has complexity <C>O(m*sqrt(n))</C>. Otherwise for general
2221+
graphs the complexity is <C>O(m*n*log(n))</C>. Here <C>n</C> is the number of vertices
2222+
and <C>m</C> is the number of edges.
2223+
2224+
<Example><![CDATA[
2225+
gap> D := DigraphFromDigraph6String("&I@EA_A?AdDp[_c??OO");
2226+
<immutable digraph with 10 vertices, 23 edges>
2227+
gap> M := DigraphMaximumMatching(D);; IsMaximalMatching(D, M);
2228+
true
2229+
gap> Length(M);
2230+
5
2231+
gap> D := Digraph([[5, 6, 7, 8], [6, 7, 8], [7, 8], [8],
2232+
> [], [], [], []]);;
2233+
gap> M := DigraphMaximumMatching(D);
2234+
[ [ 1, 5 ], [ 2, 6 ], [ 3, 7 ], [ 4, 8 ] ]
2235+
gap> D := GeneralisedPetersenGraph(IsMutableDigraph, 9, 2);
2236+
<mutable digraph with 18 vertices, 54 edges>
2237+
gap> M := DigraphMaximumMatching(D);;
2238+
gap> IsMaximalMatching(D, M);
2239+
true
2240+
gap> Length(M);
2241+
9
2242+
]]></Example>
2243+
</Description>
2244+
</ManSection>
2245+
<#/GAPDoc>

doc/oper.xml

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,43 +1658,54 @@ true
16581658
<ManSection>
16591659
<Oper Name="IsMatching" Arg="digraph, list"/>
16601660
<Oper Name="IsMaximalMatching" Arg="digraph, list"/>
1661+
<Oper Name="IsMaximumMatching" Arg="digraph, list"/>
16611662
<Oper Name="IsPerfectMatching" Arg="digraph, list"/>
16621663
<Returns><K>true</K> or <K>false</K>.</Returns>
16631664
<Description>
16641665
If <A>digraph</A> is a digraph and <A>list</A> is a list of pairs of
16651666
vertices of <A>digraph</A>, then <C>IsMatching</C> returns <K>true</K> if
1666-
<A>list</A> is a matching of <A>digraph</A>. The operations
1667-
<C>IsMaximalMatching</C> and <C>IsPerfectMatching</C> return <K>true</K> if
1668-
<A>list</A> is a maximal, or perfect, matching of <A>digraph</A>,
1669-
respectively. Otherwise, these operations return <K>false</K>. <P/>
1667+
<A>list</A> is a matching of <A>digraph</A>. The operation
1668+
<C>IsMaximalMatching</C> returns <K>true</K> if <A>list</A> is a maximal
1669+
matching, <C>IsMaximumMatching</C> returns <K>true</K> if <A>list</A> is a
1670+
maximum matching and <C>IsPerfectMatching</C> returns <K>true</K> if
1671+
<A>list</A> is a perfect, matching of <A>digraph</A>, respectively.
1672+
Otherwise, each of these operations return <K>false</K>.
1673+
<P/>
16701674

16711675
A <E>matching</E> <C>M</C> of a digraph <A>digraph</A> is a subset of the
16721676
edges of <A>digraph</A>, i.e. <C>DigraphEdges(<A>digraph</A>)</C>, such
16731677
that no pair of distinct edges in <C>M</C> are incident to the same vertex
16741678
of <A>digraph</A>. Note that this definition allows a matching to contain
16751679
loops. See <Ref Prop="DigraphHasLoops" />. The matching <C>M</C> is
1676-
<E>maximal</E> if it is contained in no larger matching of the digraph, and
1680+
<E>maximal</E> if it is contained in no larger matching of the digraph,
1681+
is <E>maximum</E> if it has the greatest cardinality among all matchings and
16771682
is <E>perfect</E> if every vertex of the digraph is incident to an edge in
1678-
the matching. Every perfect matching is maximal.
1683+
the matching. Every maximum or perfect matching is maximal. Note, however,
1684+
that not every perfect matching of digraphs with loops is maximum.
16791685

16801686
<Example><![CDATA[
1681-
gap> D := Digraph([[2], [1], [2, 3, 4], [3, 5], [1]]);
1682-
<immutable digraph with 5 vertices, 8 edges>
1687+
gap> D := Digraph([[1, 2], [1, 2], [2, 3, 4], [3, 5], [1]]);
1688+
<immutable digraph with 5 vertices, 10 edges>
16831689
gap> IsMatching(D, [[2, 1], [3, 2]]);
16841690
false
16851691
gap> edges := [[3, 2]];;
16861692
gap> IsMatching(D, edges);
16871693
true
16881694
gap> IsMaximalMatching(D, edges);
16891695
false
1690-
gap> edges := [[5, 1], [3, 3]];;
1696+
gap> edges := [[2, 1], [3, 4]];;
16911697
gap> IsMaximalMatching(D, edges);
16921698
true
16931699
gap> IsPerfectMatching(D, edges);
16941700
false
16951701
gap> edges := [[1, 2], [3, 3], [4, 5]];;
16961702
gap> IsPerfectMatching(D, edges);
16971703
true
1704+
gap> IsMaximumMatching(D, edges);
1705+
false
1706+
gap> edges := [[1, 1], [2, 2], [3, 3], [4, 5]];;
1707+
gap> IsMaximumMatching(D, edges);
1708+
true
16981709
]]></Example>
16991710
</Description>
17001711
</ManSection>

doc/z-chap4.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
<#Include Label="DigraphOutEdges">
1616
<#Include Label="IsDigraphEdge">
1717
<#Include Label="IsMatching">
18+
<#Include Label="DigraphMaximalMatching">
19+
<#Include Label="DigraphMaximumMatching">
1820
</Section>
1921

2022
<Section><Heading>Neighbours and degree</Heading>

gap/attr.gd

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,3 +114,6 @@ DeclareAttribute("DigraphMycielskianAttr", IsDigraph);
114114

115115
DeclareAttribute("DigraphCartesianProductProjections", IsDigraph);
116116
DeclareAttribute("DigraphDirectProductProjections", IsDigraph);
117+
118+
DeclareAttribute("DigraphMaximalMatching", IsDigraph);
119+
DeclareAttribute("DigraphMaximumMatching", IsDigraph);

gap/attr.gi

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2044,3 +2044,237 @@ InstallMethod(DigraphMycielskian,
20442044

20452045
InstallMethod(DigraphMycielskianAttr, "for an immutable digraph",
20462046
[IsImmutableDigraph], DigraphMycielskian);
2047+
2048+
# Uses a simple greedy algorithm.
2049+
BindGlobal("DIGRAPHS_MaximalMatching",
2050+
function(D)
2051+
local mate, u, v;
2052+
mate := ListWithIdenticalEntries(DigraphNrVertices(D), 0);
2053+
for v in DigraphVertices(D) do
2054+
if mate[v] = 0 then
2055+
for u in OutNeighboursOfVertex(D, v) do
2056+
if mate[u] = 0 then
2057+
mate[u] := v;
2058+
mate[v] := u;
2059+
break;
2060+
fi;
2061+
od;
2062+
fi;
2063+
od;
2064+
return mate;
2065+
end);
2066+
2067+
# For bipartite digraphs implements the Hopcroft-Karp matching algorithm,
2068+
# complexity O(m*sqrt(n))
2069+
BindGlobal("DIGRAPHS_BipartiteMatching",
2070+
function(D, mate)
2071+
local U, dist, inf, dfs, bfs, u;
2072+
2073+
U := DigraphBicomponents(D);
2074+
U := U[PositionMinimum(U, Length)];
2075+
2076+
bfs := function()
2077+
local v, que, q;
2078+
que := [];
2079+
for v in U do
2080+
if mate[v] = inf then
2081+
dist[v] := 0;
2082+
Add(que, v);
2083+
else
2084+
dist[v] := inf;
2085+
fi;
2086+
od;
2087+
dist[inf] := inf;
2088+
2089+
q := 1;
2090+
while q <= Length(que) do
2091+
if dist[que[q]] < dist[inf] then
2092+
for v in OutNeighborsOfVertex(D, que[q]) do
2093+
if dist[mate[v]] = inf then
2094+
dist[mate[v]] := dist[que[q]] + 1;
2095+
Add(que, mate[v]);
2096+
fi;
2097+
od;
2098+
fi;
2099+
q := q + 1;
2100+
od;
2101+
return dist[inf] <> inf;
2102+
end;
2103+
2104+
dfs := function(u)
2105+
local v;
2106+
if u = inf then
2107+
return true;
2108+
fi;
2109+
for v in OutNeighborsOfVertex(D, u) do
2110+
if dist[mate[v]] = dist[u] + 1 and dfs(mate[v]) then
2111+
mate[v] := u;
2112+
mate[u] := v;
2113+
return true;
2114+
fi;
2115+
od;
2116+
dist[u] := inf;
2117+
return false;
2118+
end;
2119+
2120+
inf := DigraphNrVertices(D) + 1;
2121+
dist := ListWithIdenticalEntries(inf, inf);
2122+
for u in [1 .. Length(mate)] do
2123+
if mate[u] = 0 then
2124+
mate[u] := inf;
2125+
fi;
2126+
od;
2127+
2128+
while bfs() do
2129+
for u in U do
2130+
if mate[u] = inf then dfs(u);
2131+
fi;
2132+
od;
2133+
od;
2134+
2135+
for u in [1 .. DigraphNrVertices(D)] do
2136+
if mate[u] = inf then
2137+
mate[u] := 0;
2138+
fi;
2139+
od;
2140+
2141+
return mate;
2142+
end);
2143+
2144+
# For general digraphs implements a modified version of Gabow's maximum matching
2145+
# algorithm, complexity O(m*n*log(n)).
2146+
BindGlobal("DIGRAPHS_GeneralMatching",
2147+
function(D, mate)
2148+
local blos, pred, time, t, tj, u, dfs, mark, blosfind;
2149+
2150+
blosfind := function(x)
2151+
if x <> blos[x] then
2152+
blos[x] := blosfind(blos[x]);
2153+
fi;
2154+
return blos[x];
2155+
end;
2156+
2157+
mark := function(v, x, b, path)
2158+
while blosfind(v) <> b do
2159+
pred[v] := x;
2160+
x := mate[v];
2161+
Add(tj, v);
2162+
Add(tj, x);
2163+
if time[x] = 0 then
2164+
t := t + 1;
2165+
time[x] := t;
2166+
Add(path, x);
2167+
fi;
2168+
v := pred[x];
2169+
od;
2170+
end;
2171+
2172+
dfs := function(v)
2173+
local x, bx, bv, b, y, z, path;
2174+
for x in OutNeighboursOfVertex(D, v) do
2175+
bv := blosfind(v);
2176+
bx := blosfind(x);
2177+
if bx <> bv then
2178+
if time[x] > 0 then
2179+
path := [];
2180+
tj := [];
2181+
if time[bx] < time[bv] then
2182+
b := bx;
2183+
mark(v, x, b, path);
2184+
else
2185+
b := bv;
2186+
mark(x, v, b, path);
2187+
fi;
2188+
for z in tj do
2189+
blos[z] := b;
2190+
od;
2191+
for z in path do
2192+
if dfs(z) then
2193+
return true;
2194+
fi;
2195+
od;
2196+
elif pred[x] = 0 then
2197+
pred[x] := v;
2198+
if mate[x] = 0 then
2199+
while x <> 0 do
2200+
y := pred[x];
2201+
v := mate[y];
2202+
mate[y] := x;
2203+
mate[x] := y;
2204+
x := v;
2205+
od;
2206+
return true;
2207+
fi;
2208+
if time[mate[x]] = 0 then
2209+
t := t + 1;
2210+
time[mate[x]] := t;
2211+
if dfs(mate[x]) then
2212+
return true;
2213+
fi;
2214+
fi;
2215+
fi;
2216+
fi;
2217+
od;
2218+
return false;
2219+
end;
2220+
2221+
time := ListWithIdenticalEntries(DigraphNrVertices(D), 0);
2222+
blos := [1 .. DigraphNrVertices(D)];
2223+
pred := ListWithIdenticalEntries(DigraphNrVertices(D), 0);
2224+
t := 0;
2225+
for u in DigraphVertices(D) do
2226+
if mate[u] = 0 then
2227+
t := t + 1;
2228+
time[u] := t;
2229+
if dfs(u) then
2230+
time := ListWithIdenticalEntries(DigraphNrVertices(D), 0);
2231+
blos := [1 .. DigraphNrVertices(D)];
2232+
pred := ListWithIdenticalEntries(DigraphNrVertices(D), 0);
2233+
fi;
2234+
fi;
2235+
od;
2236+
2237+
return mate;
2238+
end);
2239+
2240+
BindGlobal("DIGRAPHS_MateToMatching",
2241+
function(D, mate)
2242+
local u, M;
2243+
M := [];
2244+
for u in DigraphVertices(D) do
2245+
if u <= mate[u] then
2246+
if IsDigraphEdge(D, u, mate[u]) then
2247+
Add(M, [u, mate[u]]);
2248+
elif IsDigraphEdge(D, mate[u], u) then
2249+
Add(M, [mate[u], u]);
2250+
fi;
2251+
fi;
2252+
od;
2253+
return Set(M);
2254+
end);
2255+
2256+
InstallMethod(DigraphMaximalMatching, "for a digraph", [IsDigraph],
2257+
D -> DIGRAPHS_MateToMatching(D, DIGRAPHS_MaximalMatching(D)));
2258+
2259+
InstallMethod(DigraphMaximumMatching, "for a digraph", [IsDigraph],
2260+
function(D)
2261+
local mateG, mateD, G, M, i, lab;
2262+
G := DigraphImmutableCopy(D);
2263+
G := InducedSubdigraph(G, Difference(DigraphVertices(G), DigraphLoops(G)));
2264+
lab := DigraphVertexLabels(G);
2265+
G := DigraphSymmetricClosure(G);
2266+
mateG := DIGRAPHS_MaximalMatching(G);
2267+
if IsBipartiteDigraph(G) then
2268+
mateG := DIGRAPHS_BipartiteMatching(G, mateG);
2269+
else
2270+
mateG := DIGRAPHS_GeneralMatching(G, mateG);
2271+
fi;
2272+
mateD := ListWithIdenticalEntries(DigraphNrVertices(D), 0);
2273+
for i in DigraphVertices(G) do
2274+
if mateG[i] <> 0 then
2275+
mateD[lab[i]] := lab[mateG[i]];
2276+
fi;
2277+
od;
2278+
M := List(DigraphLoops(D), x -> [x, x]);
2279+
return Union(M, DIGRAPHS_MateToMatching(D, mateD));
2280+
end);

gap/oper.gd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ DeclareOperation("IsUndirectedSpanningForest", [IsDigraph, IsDigraph]);
8888

8989
DeclareOperation("IsMatching", [IsDigraph, IsHomogeneousList]);
9090
DeclareOperation("IsMaximalMatching", [IsDigraph, IsHomogeneousList]);
91+
DeclareOperation("IsMaximumMatching", [IsDigraph, IsHomogeneousList]);
9192
DeclareOperation("IsPerfectMatching", [IsDigraph, IsHomogeneousList]);
9293

9394
# 9. Connectivity . . .

0 commit comments

Comments
 (0)