/// <summary>Contrived example to test blossoming.</summary> [TestMethod()] public void Blossom() { Graph g = Graph.FromSmiles("CCCCCC1CCCC1CC"); Matching m = Matching.CreateEmpty(g); // initial matching from double-bonds (size = 5) m.Match(1, 2); m.Match(3, 4); m.Match(5, 6); m.Match(7, 8); m.Match(9, 10); MaximumMatching.Maximise(g, m, 10); // once maximised the matching has been augmented such that there // are now six disjoint edges (only possibly by contracting blossom) Assert.AreEqual(6, m.GetMatches().Count()); Assert.IsTrue(Compares.AreOrderLessDeepEqual( new[] { Tuple.Of(0, 1), Tuple.Of(2, 3), Tuple.Of(4, 5), Tuple.Of(6, 7), Tuple.Of(8, 9), Tuple.Of(10, 11), }, m.GetMatches())); }
/// <summary> /// An edge was found which connects two 'even' vertices in the forest. If /// the vertices have the same root we have a blossom otherwise we have /// identified an augmenting path. This method checks for these cases and /// responds accordingly. /// <para> /// If an augmenting path was found - then it's edges are alternated and the /// method returns true. Otherwise if a blossom was found - it is contracted /// and the search continues. /// </para> /// </summary> /// <param name="v">endpoint of an edge</param> /// <param name="w">another endpoint of an edge</param> /// <returns>a path was augmented</returns> private bool Check(int v, int w) { // self-loop (within blossom) ignored if (uf.Connected(v, w)) { return(false); } vAncestors.SetAll(false); wAncestors.SetAll(false); int vCurr = v; int wCurr = w; // walk back along the trees filling up 'vAncestors' and 'wAncestors' // with the vertices in the tree - vCurr and wCurr are the 'even' parents // from v/w along the tree while (true) { vCurr = Parent(vAncestors, vCurr); wCurr = Parent(wAncestors, wCurr); // v and w lead to the same root - we have found a blossom. We // travelled all the way down the tree thus vCurr (and wCurr) are // the base of the blossom if (vCurr == wCurr) { Blossom(v, w, vCurr); return(false); } // we are at the root of each tree and the roots are different, we // have found and augmenting path if (uf.Find(even[vCurr]) == vCurr && uf.Find(even[wCurr]) == wCurr) { Augment(v); Augment(w); matching.Match(v, w); return(true); } // the current vertex in 'v' can be found in w's ancestors they must // share a root - we have found a blossom whose base is 'vCurr' if (wAncestors[vCurr]) { Blossom(v, w, vCurr); return(false); } // the current vertex in 'w' can be found in v's ancestors they must // share a root, we have found a blossom whose base is 'wCurr' if (vAncestors[wCurr]) { Blossom(v, w, wCurr); return(false); } } }
public void Adjusted_other_invalid() { Matching matching = Matching.CreateEmpty(Graph.FromSmiles("CCCCC")); matching.Match(0, 1); matching.Match(2, 3); matching.Match(1, 2); // 0-1 and 2-3 should not be matching.Other(0); }
[TestMethod()] public void Adjusted_other() { Matching matching = Matching.CreateEmpty(Graph.FromSmiles("CCCCC")); matching.Match(0, 1); matching.Match(2, 3); matching.Match(1, 2); // 0-1 and 2-3 should not be Assert.AreEqual(matching.Other(1), 2); Assert.AreEqual(matching.Other(2), 1); }
[TestMethod()] public void Basic() { Matching matching = Matching.CreateEmpty(Graph.FromSmiles("CCCCC")); matching.Match(0, 1); matching.Match(2, 3); Assert.AreEqual(2, matching.GetMatches().Count()); Assert.IsTrue(Compares.AreOrderLessDeepEqual( new[] { Tuple.Of(0, 1), Tuple.Of(2, 3) }, matching.GetMatches())); }
[TestMethod()] public void Adjusted_contains() { Matching matching = Matching.CreateEmpty(Graph.FromSmiles("CCCCC")); matching.Match(0, 1); matching.Match(2, 3); matching.Match(1, 2); // 0-1 and 2-3 should not be Assert.IsFalse(matching.Unmatched(1)); Assert.IsFalse(matching.Unmatched(2)); Assert.IsTrue(matching.Unmatched(0)); Assert.IsTrue(matching.Unmatched(3)); }
public void Adjusted() { Matching matching = Matching.CreateEmpty(Graph.FromSmiles("CCCCC")); matching.Match(0, 1); matching.Match(2, 3); matching.Match(1, 2); // 0-1 and 2-3 should not be Assert.IsFalse(Compares.AreOrderLessDeepEqual( new[] { Tuple.Of(0, 1), Tuple.Of(2, 3) }, matching.GetMatches())); Assert.AreEqual(1, matching.GetMatches().Count()); Assert.IsTrue(Compares.AreOrderLessDeepEqual( new[] { Tuple.Of(1, 2) }, matching.GetMatches())); }
/// <summary> /// Create an arbitrary matching on the subset of vertices ('s') of provided /// graph. The provided matching should be empty. /// /// <param name="g">graph to match</param> /// <param name="m">empty matching (presumed)</param> /// <param name="s">subset of vertices</param> /// <returns>number of vertices matched</returns> /// </summary> public static int Initial(Graph g, Matching m, BitArray s) { int nMatched = 0; for (int v = BitArrays.NextSetBit(s, 0); v >= 0; v = BitArrays.NextSetBit(s, v + 1)) { // skip if already matched if (m.Matched(v)) { continue; } // find a single edge which is not matched and match it int d = g.Degree(v); for (int j = 0; j < d; ++j) { Edge e = g.EdgeAt(v, j); int w = e.Other(v); if ((e.Bond != Bond.Single) && m.Unmatched(w) && s[w]) { m.Match(v, w); nMatched += 2; break; } } } return(nMatched); }
[TestMethod()] public void Simple_augment() { Graph g = Graph.FromSmiles("cccc"); Matching m = Matching.CreateEmpty(g); m.Match(1, 2); MaximumMatching.Maximise(g, m, 2); Assert.IsTrue(Compares.AreOrderLessDeepEqual( new[] { Tuple.Of(0, 1), Tuple.Of(2, 3) }, m.GetMatches())); }
public void Napthalene_augment() { Graph g = Graph.FromSmiles("C1C=CC2=CCC=CC2=C1"); Matching m = Matching.CreateEmpty(g); m.Match(1, 2); m.Match(3, 4); m.Match(6, 7); m.Match(8, 9); MaximumMatching.Maximise(g, m, 8); Assert.IsTrue(Compares.AreOrderLessDeepEqual( new[] { Tuple.Of(0, 1), Tuple.Of(2, 3), Tuple.Of(4, 5), Tuple.Of(6, 7), Tuple.Of(8, 9), }, m.GetMatches())); }
[TestMethod()] public void Furan_augment() { Graph g = Graph.FromSmiles("o1cccc1"); IntSet s = IntSet.AllOf(1, 2, 3, 4); // exclude the oxygen Matching m = Matching.CreateEmpty(g); m.Match(2, 3); MaximumMatching.Maximise(g, m, 2, s); Assert.IsTrue(Compares.AreOrderLessDeepEqual( new[] { Tuple.Of(1, 2), Tuple.Of(3, 4) }, m.GetMatches())); }
[TestMethod()] public void Simple_augment_subset() { Graph g = Graph.FromSmiles("cccc"); Matching m = Matching.CreateEmpty(g); m.Match(1, 2); // no vertex '3' matching can not be improved MaximumMatching.Maximise(g, m, 2, IntSet.AllOf(0, 1, 2)); Assert.IsTrue(Compares.AreOrderLessDeepEqual( new[] { Tuple.Of(1, 2) }, m.GetMatches())); }
/// <summary> /// When precisely two vertices are unmatched we only need to find a single /// augmenting path. Rather than run through edmonds with blossoms etc we /// simple do a targest DFS for the path. /// /// <param name="g">graph</param> /// <param name="m">matching</param> /// <param name="nMatched">current matching cardinality must be |s|-nMathced == 2</param> /// <param name="s">subset size</param> /// <returns>new match cardinality</returns> /// </summary> public static int AugmentOnce(Graph g, Matching m, int nMatched, BitArray s) { int vStart = BitArrays.NextSetBit(s, 0); while (vStart >= 0) { if (!m.Matched(vStart)) { break; } vStart = BitArrays.NextSetBit(s, vStart + 1); } int vEnd = BitArrays.NextSetBit(s, vStart + 1); while (vEnd >= 0) { if (!m.Matched(vEnd)) { break; } vEnd = BitArrays.NextSetBit(s, vEnd + 1); } // find an augmenting path between vStart and vEnd int[] path = new int[g.Order]; int len = FindPath(g, vStart, vEnd, s, path, 0, m, false); if (len > 0) { // augment for (int i = 0; i < len; i += 2) { m.Match(path[i], path[i + 1]); } nMatched += 2; } return(nMatched); }
public static int DfsVisit(Graph g, int v, Matching m, BitArray unvisited, bool match) { unvisited.Set(v, false); int nMatched = 0; int d = g.Degree(v); while (--d >= 0) { int w = g.EdgeAt(v, d).Other(v); if (unvisited[w]) { if (match) { m.Match(v, w); return(2 + DfsVisit(g, w, m, unvisited, false)); } else { nMatched += DfsVisit(g, w, m, unvisited, true); } } } return(nMatched); }