public static Graph GenerateKekuleForm(Graph g, BitArray subset, BitArray aromatic, bool inplace) { // make initial (empty) matching - then improve it, first // by matching the first edges we find, most of time this // gives us a perfect matching if not we maximise it // with Edmonds' algorithm Matching m = Matching.CreateEmpty(g); int n = BitArrays.Cardinality(subset); int nMatched = ArbitraryMatching.Initial(g, m, subset); if (nMatched < n) { if (n - nMatched == 2) { nMatched = ArbitraryMatching.AugmentOnce(g, m, nMatched, subset); } if (nMatched < n) { nMatched = MaximumMatching.Maximise(g, m, nMatched, IntSet.FromBitArray(subset)); } if (nMatched < n) { throw new InvalidSmilesException("Could not Kekulise"); } } return(inplace ? Assign(g, subset, aromatic, m) : CopyAndAssign(g, subset, aromatic, m)); }
/// <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())); }
public void Simple_maximal() { Graph g = Graph.FromSmiles("cccc"); Matching m = MaximumMatching.Maximal(g); Assert.IsTrue(Compares.AreOrderLessDeepEqual( new[] { Tuple.Of(0, 1), Tuple.Of(2, 3) }, m.GetMatches())); }
public void Furan() { Graph g = Graph.FromSmiles("o1cccc1"); IntSet s = IntSet.AllOf(1, 2, 3, 4); // exclude the oxygen Matching m = Matching.CreateEmpty(g); MaximumMatching.Maximise(g, m, 0, s); Assert.IsTrue(Compares.AreOrderLessDeepEqual( new[] { Tuple.Of(1, 2), Tuple.Of(3, 4) }, m.GetMatches())); }
[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())); }
[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())); }
[TestMethod()] public void Imidazole() { Graph g = Graph.FromSmiles("[nH]1ccnc1"); Matching m = Matching.CreateEmpty(g); MaximumMatching.Maximise(g, m, 0, IntSet.AllOf(1, 2, 3, 4)); // not the 'nH' Assert.IsTrue(Compares.AreOrderLessDeepEqual( new[] { Tuple.Of(1, 2), Tuple.Of(3, 4) }, m.GetMatches())); }
[TestMethod()] public void Quinone() { Graph g = Graph.FromSmiles("Oc1ccc(o)cc1"); Matching m = MaximumMatching.Maximal(g); Assert.IsTrue(Compares.AreOrderLessDeepEqual( new[] { Tuple.Of(0, 1), Tuple.Of(2, 3), Tuple.Of(4, 5), Tuple.Of(6, 7), }, m.GetMatches())); }
public void Quinone_subset() { Graph g = Graph.FromSmiles("Oc1ccc(o)cc1"); // mocks the case where the oxygen atoms are already double bonded - we // therefore don't include those of the adjacent carbons in the vertex // subset to be matched Matching m = Matching.CreateEmpty(g); MaximumMatching.Maximise(g, m, 0, IntSet.AllOf(2, 3, 6, 7)); Assert.IsTrue(Compares.AreOrderLessDeepEqual( new[] { Tuple.Of(2, 3), Tuple.Of(6, 7), }, m.GetMatches())); }
[TestMethod()] public void Benzimidazole() { Graph g = Graph.FromSmiles("c1nc2ccccc2[nH]1"); Matching m = Matching.CreateEmpty(g); MaximumMatching.Maximise(g, m, 0, IntSet.NoneOf(8)); // not the 'nH' Assert.IsTrue(Compares.AreOrderLessDeepEqual( new[] { Tuple.Of(0, 1), Tuple.Of(2, 3), Tuple.Of(4, 5), Tuple.Of(6, 7), }, 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())); }
public static Graph Resonate(Graph g, BitArray cyclic, bool ordered) { BitArray subset = new BitArray(g.Order); for (int u = BitArrays.NextSetBit(cyclic, 0); u >= 0; u = BitArrays.NextSetBit(cyclic, u + 1)) { // candidates must have a bonded // valence of one more than their degree // and in a ring int uExtra = g.BondedValence(u) - g.Degree(u); if (uExtra > 0) { int other = -1; Edge target = null; int d = g.Degree(u); for (int j = 0; j < d; ++j) { Edge e = g.EdgeAt(u, j); int v = e.Other(u); // check for bond validity if (e.Bond.Order == 2) { int vExtra = g.BondedValence(v) - g.Degree(v); if (cyclic[v] && vExtra > 0) { if (HasAdjDirectionalLabels(g, e, cyclic) && !InSmallRing(g, e)) { other = -1; break; } if (vExtra > 1 && HasAdditionalCyclicDoubleBond(g, cyclic, u, v)) { other = -1; break; } if (other == -1) { other = v; // first one target = e; } else { other = -2; // found more than one } } // only one double bond don't check any more if (uExtra == 1) { break; } } } if (other >= 0) { subset.Set(u, true); subset.Set(other, true); target.SetBond(Bond.Implicit); } } } if (!ordered) { g = g.Sort(new Graph.CanOrderFirst()); } Matching m = Matching.CreateEmpty(g); int n = BitArrays.Cardinality(subset); int nMatched = ArbitraryMatching.Dfs(g, m, subset); if (nMatched < n) { if (n - nMatched == 2) { nMatched = ArbitraryMatching.AugmentOnce(g, m, nMatched, subset); } if (nMatched < n) { nMatched = MaximumMatching.Maximise(g, m, nMatched, IntSet.FromBitArray(subset)); } if (nMatched < n) { throw new ApplicationException("Could not Kekulise"); } } // assign new double bonds for (int v = BitArrays.NextSetBit(subset, 0); v >= 0; v = BitArrays.NextSetBit(subset, v + 1)) { int w = m.Other(v); subset.Set(w, false); g.CreateEdge(v, w).SetBond(Bond.Double); } return(g); }
/// <summary> /// Utility to maximise an existing matching of the provided graph. /// </summary> /// <param name="g">a graph</param> /// <param name="m">matching on the graph, will me modified</param> /// <param name="n">current matching cardinality</param> /// <param name="s">subset of vertices to match</param> /// <returns>the maximal matching on the graph</returns> public static int Maximise(Graph g, Matching m, int n, IntSet s) { MaximumMatching mm = new MaximumMatching(g, m, n, s); return(mm.nMatched); }