private void SetDirection(Edge e, int u, Bond b) { if (e.Either() == u) { e.SetBond(b); } else { e.SetBond(b.Inverse()); } }
// invariant, m is a perfect matching private static Graph Assign(Graph g, BitArray subset, BitArray aromatic, Matching m) { g.SetFlags(g.GetFlags() & ~Graph.HAS_AROM); for (int u = BitArrays.NextSetBit(aromatic, 0); u >= 0; u = BitArrays.NextSetBit(aromatic, u + 1)) { g.SetAtom(u, g.GetAtom(u).AsAliphaticForm()); int deg = g.Degree(u); for (int j = 0; j < deg; ++j) { Edge e = g.EdgeAt(u, j); int v = e.Other(u); if (v < u) { var aa = e.Bond; if (aa == Bond.Single) { if (aromatic[u] && aromatic[v]) { e.SetBond(Bond.Single); } else { e.SetBond(Bond.Implicit); } } else if (aa == Bond.Aromatic) { if (subset[u] && m.Other(u) == v) { e.SetBond(Bond.DoubleAromatic); g.UpdateBondedValence(u, +1); g.UpdateBondedValence(v, +1); } else if (aromatic[v]) { e.SetBond(Bond.ImplicitAromatic); } else { e.SetBond(Bond.Implicit); } } else if (aa == Bond.Implicit) { if (subset[u] && m.Other(u) == v) { e.SetBond(Bond.DoubleAromatic); g.UpdateBondedValence(u, +1); g.UpdateBondedValence(v, +1); } else if (aromatic[u] && aromatic[v]) { e.SetBond(Bond.ImplicitAromatic); } } } } } return(g); }
/// <summary> /// Add an edge to the graph. /// </summary> /// <param name="e">the edge to add</param> /// <returns>graph builder for adding more atoms/connections</returns> public GraphBuilder Add(Edge e) { Bond b = e.Bond; int u = e.Either(); int v = e.Other(u); if (b == Bond.Single && (!g.GetAtom(u).IsAromatic() || !g.GetAtom(v).IsAromatic())) { e.SetBond(Bond.Implicit); } else if (b == Bond.Aromatic && g.GetAtom(u).IsAromatic() && g.GetAtom(v).IsAromatic()) { e.SetBond(Bond.Implicit); } g.AddEdge(e); valence[u] += b.Order; valence[v] += b.Order; return(this); }
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); }
private void AssignDirectionalLabels() { if (!builders.Any()) { return; } // handle extended geometric configurations first var buildersToRemove = new List <GeometricBuilder>(); foreach (var builder in builders) { if (!builder.Extended) { continue; } buildersToRemove.Add(builder); Edge e = FindDoubleBond(g, builder.u); Edge f = FindDoubleBond(g, builder.v); if (e == null || f == null) { continue; } Edge eRef = g.CreateEdge(builder.u, builder.X); Edge fRef = g.CreateEdge(builder.v, builder.Y); Edge eLab = FindBondToLabel(g, builder.u); Edge fLab = FindBondToLabel(g, builder.v); // adjust for reference Configuration.ConfigurationDoubleBond config = builder.c; if ((eLab == eRef) != (fRef == fLab)) { if (config == Configuration.ConfigurationDoubleBond.Together) { config = Configuration.ConfigurationDoubleBond.Opposite; } else if (config == Configuration.ConfigurationDoubleBond.Opposite) { config = Configuration.ConfigurationDoubleBond.Together; } } if (eLab.Bond.IsDirectional) { if (fLab.Bond.IsDirectional) { // can't do anything, may be incorrect } else { if (config == Configuration.ConfigurationDoubleBond.Together) { SetDirection(fLab, builder.v, eLab.GetBond(builder.u)); } else if (config == Configuration.ConfigurationDoubleBond.Opposite) { SetDirection(fLab, builder.v, eLab.GetBond(builder.u)); } } } else { if (fLab.Bond.IsDirectional) { if (config == Configuration.ConfigurationDoubleBond.Together) { SetDirection(eLab, builder.v, fLab.GetBond(builder.u)); } else if (config == Configuration.ConfigurationDoubleBond.Opposite) { SetDirection(eLab, builder.v, fLab.GetBond(builder.u)); } } else { SetDirection(eLab, builder.u, Bond.Down); if (config == Configuration.ConfigurationDoubleBond.Together) { SetDirection(fLab, builder.v, Bond.Down); } else if (config == Configuration.ConfigurationDoubleBond.Opposite) { SetDirection(fLab, builder.v, Bond.Up); } } } } foreach (var builder in buildersToRemove) { builders.Remove(builder); } if (!builders.Any()) { return; } // store the vertices which are adjacent to pi bonds with a config BitArray pibonded = new BitArray(g.Order); BitArray unspecified = new BitArray(g.Order); var unspecEdges = new HashSet <Edge>(); // clear existing directional labels, if build is called multiple times // this can cause problems if (g.GetFlags(Graph.HAS_BND_STRO) != 0) { foreach (Edge edge in g.Edges) { if (edge.Bond.IsDirectional) { edge.SetBond(Bond.Implicit); } } } foreach (Edge e in g.Edges) { int u = e.Either(); int v = e.Other(u); if (e.Bond.Order == 2 && g.Degree(u) >= 2 && g.Degree(v) >= 2) { unspecified.Set(u, true); unspecified.Set(v, true); pibonded.Set(u, true); pibonded.Set(v, true); unspecEdges.Add(e); } } foreach (var builder in builders) { g.AddFlags(Graph.HAS_BND_STRO); // unspecified only used for getting not setting configuration if (builder.c == Configuration.ConfigurationDoubleBond.Unspecified) { continue; } CheckGeometricBuilder(builder); // check required vertices are adjacent int u = builder.u, v = builder.v, x = builder.X, y = builder.Y; if (x == y) { continue; } unspecEdges.Remove(g.CreateEdge(u, v)); unspecified.Set(u, false); unspecified.Set(v, false); Fix(g, u, v, pibonded); Fix(g, v, u, pibonded); Bond first = FirstDirectionalLabel(u, x, pibonded); Bond second = builder.c == Configuration.ConfigurationDoubleBond.Together ? first : first.Inverse(); // check if the second label would cause a conflict if (CheckDirectionalAssignment(second, v, y, pibonded)) { // okay to assign the labels as they are g.Replace(g.CreateEdge(u, x), new Edge(u, x, first)); g.Replace(g.CreateEdge(v, y), new Edge(v, y, second)); } // there will be a conflict - check if we invert the first one... else if (CheckDirectionalAssignment(first.Inverse(), u, x, pibonded)) { g.Replace(g.CreateEdge(u, x), new Edge(u, x, (first = first.Inverse()))); g.Replace(g.CreateEdge(v, y), new Edge(v, y, (second = second.Inverse()))); } else { BitArray visited = new BitArray(g.Order); visited.Set(v, true); InvertExistingDirectionalLabels(pibonded, visited, v, u); if (!CheckDirectionalAssignment(first, u, x, pibonded) || !CheckDirectionalAssignment(second, v, y, pibonded)) { throw new ArgumentException("cannot assign geometric configuration"); } g.Replace(g.CreateEdge(u, x), new Edge(u, x, first)); g.Replace(g.CreateEdge(v, y), new Edge(v, y, second)); } // propagate bond directions to other adjacent bonds foreach (var e in g.GetEdges(u)) { if (e.Bond != Bond.Double && !e.Bond.IsDirectional) { e.SetBond(e.Either() == u ? first.Inverse() : first); } } foreach (var e in g.GetEdges(v)) { if (e.Bond != Bond.Double && !e.Bond.IsDirectional) { e.SetBond(e.Either() == v ? second.Inverse() : second); } } } // unspecified pibonds should "not" have a configuration, if they // do we try to eliminate it foreach (Edge unspecEdge in unspecEdges) { int u = unspecEdge.Either(); int v = unspecEdge.Other(u); // no problem if one side isn't defined if (!HasDirectional(g, u) || !HasDirectional(g, v)) { continue; } foreach (Edge e in g.GetEdges(u)) { if (IsRedundantDirectionalEdge(g, e, unspecified)) { e.SetBond(Bond.Implicit); } } if (!HasDirectional(g, u)) { continue; } foreach (Edge e in g.GetEdges(v)) { if (IsRedundantDirectionalEdge(g, e, unspecified)) { e.SetBond(Bond.Implicit); } } // if (hasDirectional(g, v)) // could generate warning! } }