/// <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); }
/// <summary> /// Attempt to augment the matching such that it is perfect over the subset /// of vertices in the provided graph. /// </summary> /// <param name="graph">adjacency list representation of graph</param> /// <param name="subset">subset of vertices</param> /// <returns>the matching was perfect</returns> /// <exception cref="ArgumentException">the graph was a different size to the matching capacity</exception> public bool Perfect(int[][] graph, BitArray subset) { if (graph.Length != match.Length || BitArrays.Cardinality(subset) > graph.Length) { throw new ArgumentException("graph and matching had different capacity"); } // and odd set can never provide a perfect matching if ((BitArrays.Cardinality(subset) & 0x1) == 0x1) { return(false); } // arbitrary matching was perfect if (ArbitaryMatching(graph, subset)) { return(true); } EdmondsMaximumMatching.Maxamise(this, graph, subset); // the matching is imperfect if any vertex was for (int v = BitArrays.NextSetBit(subset, 0); v >= 0; v = BitArrays.NextSetBit(subset, v + 1)) { if (Unmatched(v)) { return(false); } } return(true); }
// 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> /// List all differences between the two bit vectors. Unlike /// <see cref="ListDifferences(BitArray, BitArray)"/> which only list /// those which are set in <paramref name="s"/> but not in <paramref name="t"/>. /// </summary> /// <param name="s">a bit vector</param> /// <param name="t">another bit vector</param> /// <returns>all differences between <paramref name="s"/> and <paramref name="t"/></returns> public static IReadOnlyCollection <int> Differences(BitArray s, BitArray t) { var u = (BitArray)s.Clone(); var v = (BitArray)t.Clone(); var len = Math.Max(u.Length, v.Length); if (u.Length < len) { u.Length = len; } if (v.Length < len) { v.Length = len; } u.Xor(v); var differences = new SortedSet <int>(); for (int i = BitArrays.NextSetBit(u, 0); i >= 0; i = BitArrays.NextSetBit(u, i + 1)) { differences.Add(i); } return(differences); }
public void Flag(bool[] mark) { mark[u] = true; for (int i = BitArrays.NextSetBit(xs, 0); i >= 0; i = BitArrays.NextSetBit(xs, i + 1)) { mark[i] = true; } }
public override int[] ToArray() { int[] xs = new int[Count]; int n = 0; for (int i = BitArrays.NextSetBit(set, 0); i >= 0; i = BitArrays.NextSetBit(set, i + 1)) { xs[n++] = i; } return(xs); }
/// <summary> /// Converts a CDKRGraph bitset (set of CDKRNode) /// to a list of CDKRMap that represents the /// mapping between to substructures in G1 and G2 /// (the projection of the CDKRGraph bitset on G1 /// and G2). /// /// <param name="set">the BitArray</param> /// <returns>the CDKRMap list</returns> /// </summary> public IReadOnlyList <CDKRMap> BitSetToRMap(BitArray set) { List <CDKRMap> rMapList = new List <CDKRMap>(); for (int x = BitArrays.NextSetBit(set, 0); x >= 0; x = BitArrays.NextSetBit(set, x + 1)) { CDKRNode xNode = Graph[x]; rMapList.Add(xNode.RMap); } return(rMapList); }
///////////////////////////////// // BitArray tools /// <summary> /// Projects a RGraph bitset on the source graph G1. /// </summary> /// <param name="set">RGraph BitArray to project</param> /// <returns>The associate BitArray in G1</returns> public BitArray ProjectG1(BitArray set) { BitArray projection = new BitArray(FirstGraphSize); RNode xNode = null; for (int x = BitArrays.NextSetBit(set, 0); x >= 0; x = BitArrays.NextSetBit(set, x + 1)) { xNode = (RNode)graph[x]; projection.Set(xNode.RMap.Id1, true); } return(projection); }
/// <summary> /// Projects a CDKRGraph bitset on the source graph G2. /// </summary> /// <param name="set">CDKRGraph BitArray to project</param> /// <returns>The associate BitArray in G2</returns> public BitArray ProjectG2(BitArray set) { BitArray projection = new BitArray(SecondGraphSize); CDKRNode xNode = null; for (int x = BitArrays.NextSetBit(set, 0); x >= 0; x = BitArrays.NextSetBit(set, x + 1)) { xNode = Graph[x]; projection.Set(xNode.RMap.Id2, true); } return(projection); }
public bool Encode(long[] current, long[] next) { bool configured = false; for (int i = BitArrays.NextSetBit(unconfigured, 0); i >= 0; i = BitArrays.NextSetBit(unconfigured, i + 1)) { if (encoders[i].Encode(current, next)) { unconfigured.Set(i, false); // don't configure again (unless reset) configured = true; } } return(configured); }
/// <summary> /// Build an indexed lookup of vertex color. The vertex color indicates which /// cycle a given vertex belongs. If a vertex belongs to more then one cycle /// it is colored '0'. If a vertex belongs to no cycle it is colored '-1'. /// </summary> /// <returns>vertex colors</returns> private int[] BuildVertexColor() { int[] color = new int[g.Count]; int n = 1; Arrays.Fill(color, -1); foreach (var cycle in cycles) { for (int i = BitArrays.NextSetBit(cycle, 0); i >= 0; i = BitArrays.NextSetBit(cycle, i + 1)) { color[i] = color[i] < 0 ? n : 0; } n++; } return(color); }
/// <summary> /// Assign a Kekulé representation to the aromatic systems of a compound. /// </summary> /// <param name="ac">structural representation</param> /// <exception cref="CDKException">a Kekulé form could not be assigned</exception> public static void Kekulize(IAtomContainer ac) { // storage of pairs of atoms that have pi-bonded var matching = Matching.WithCapacity(ac.Atoms.Count); // exract data structures for efficient access var atoms = ac.Atoms.ToArray(); var bonds = EdgeToBondMap.WithSpaceFor(ac); var graph = GraphUtil.ToAdjList(ac, bonds); // determine which atoms are available to have a pi bond placed var available = IsAvailable(graph, atoms, bonds); // attempt to find a perfect matching such that a pi bond is placed // next to each available atom. if not found the solution is ambiguous if (!matching.Perfect(graph, available)) { throw new CDKException("Cannot assign Kekulé structure without randomly creating radicals."); } // propagate bond order information from the matching foreach (var bond in ac.Bonds) { if (bond.Order == BondOrder.Unset && bond.IsAromatic) { bond.Order = BondOrder.Single; } } for (int v = BitArrays.NextSetBit(available, 0); v >= 0; v = BitArrays.NextSetBit(available, v + 1)) { var w = matching.Other(v); var bond = bonds[v, w]; // sanity check, something wrong if this happens if (bond.Order.Numeric() > 1) { throw new CDKException("Cannot assign Kekulé structure, non-sigma bond order has already been assigned?"); } bond.Order = BondOrder.Double; available.Set(w, false); } }
/// <summary> /// Check if it is possible to assign electrons to the subgraph (specified by /// the set bits in of <paramref name="bs"/>). Each connected subset is counted up and /// checked for odd cardinality. /// </summary> /// <param name="g"> graph</param> /// <param name="bs">binary set indicated vertices for the subgraph</param> /// <returns>there is an odd cardinality subgraph</returns> private static bool ContainsOddCardinalitySubgraph(Graph g, BitArray bs) { // mark visited those which are not in any subgraph bool[] visited = new bool[g.Order]; for (int i = BitArrays.NextClearBit(bs, 0); i < g.Order; i = BitArrays.NextClearBit(bs, i + 1)) { visited[i] = true; } // from each unvisited vertices visit the connected vertices and count // how many there are in this component. if there is an odd number there // is no assignment of double bonds possible for (int i = BitArrays.NextSetBit(bs, 0); i >= 0; i = BitArrays.NextSetBit(bs, i + 1)) { if (!visited[i] && IsOdd(Visit(g, i, 0, visited))) { return(true); } } return(false); }
/// <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 Dfs(Graph g, Matching m, BitArray s) { int nMatched = 0; BitArray unvisited = (BitArray)s.Clone(); // visit those with degree 1 first and expand out matching for (int v = BitArrays.NextSetBit(unvisited, 0); v >= 0; v = BitArrays.NextSetBit(unvisited, v + 1)) { if (!m.Matched(v)) { int cnt = 0; int d = g.Degree(v); while (--d >= 0) { int w = g.EdgeAt(v, d).Other(v); if (unvisited[w]) { ++cnt; } } if (cnt == 1) { nMatched += DfsVisit(g, v, m, unvisited, true); } } } // now those which aren't degree 1 for (int v = BitArrays.NextSetBit(unvisited, 0); v >= 0; v = BitArrays.NextSetBit(unvisited, v + 1)) { if (!m.Matched(v)) { nMatched += DfsVisit(g, v, m, unvisited, true); } } return(nMatched); }
/// <summary> /// Create the topologies (stereo configurations) for the chemical graph. The /// topologies define spacial arrangement around atoms. /// </summary> private void CreateTopologies(CharBuffer buffer) { // create topologies (stereo configurations) foreach (var e in configurations) { AddTopology(e.Key, Topology.ToExplicit(g, e.Key, e.Value)); } for (int v = BitArrays.NextSetBit(checkDirectionalBonds, 0); v >= 0; v = BitArrays.NextSetBit(checkDirectionalBonds, v + 1)) { int nUpV = 0; int nDownV = 0; int nUpW = 0; int nDownW = 0; int w = -1; { int d = g.Degree(v); for (int j = 0; j < d; ++j) { Edge e = g.EdgeAt(v, j); Bond bond = e.GetBond(v); if (bond == Bond.Up) { nUpV++; } else if (bond == Bond.Down) { nDownV++; } else if (bond == Bond.Double) { w = e.Other(v); } } } if (w < 0) { continue; } BitArrays.EnsureCapacity(checkDirectionalBonds, w + 1); checkDirectionalBonds.Set(w, false); { int d = g.Degree(w); for (int j = 0; j < d; ++j) { Edge e = g.EdgeAt(w, j); Bond bond = e.GetBond(w); if (bond == Bond.Up) { nUpW++; } else if (bond == Bond.Down) { nDownW++; } } } if (nUpV + nDownV == 0 || nUpW + nDownW == 0) { continue; } if (nUpV > 1) { throw new InvalidSmilesException("Multiple directional bonds on atom " + v, buffer); } if (nDownV > 1) { throw new InvalidSmilesException("Multiple directional bonds on atom " + v, buffer); } if (nUpW > 1) { throw new InvalidSmilesException("Multiple directional bonds on atom " + w, buffer); } if (nDownW > 1) { throw new InvalidSmilesException("Multiple directional bonds on atom " + w, buffer); } } }
/// <summary> /// Assign an arbitrary matching that covers the subset of vertices. /// </summary> /// <param name="graph">adjacency list representation of graph</param> /// <param name="subset">subset of vertices in the graph</param> /// <returns>the matching was perfect</returns> internal bool ArbitaryMatching(int[][] graph, BitArray subset) { BitArray unmatched = new BitArray(subset.Length); // indicates the deg of each vertex in unmatched subset int[] deg = new int[graph.Length]; // queue/stack of vertices with deg1 vertices int[] deg1 = new int[graph.Length]; int nd1 = 0, nMatched = 0; for (int v = BitArrays.NextSetBit(subset, 0); v >= 0; v = BitArrays.NextSetBit(subset, v + 1)) { if (Matched(v)) { Trace.Assert(subset[Other(v)]); nMatched++; continue; } unmatched.Set(v, true); foreach (var w in graph[v]) { if (subset[w] && Unmatched(w)) { deg[v]++; } } if (deg[v] == 1) { deg1[nd1++] = v; } } while (!BitArrays.IsEmpty(unmatched)) { int v = -1; // attempt to select a vertex with degree = 1 (in matched set) while (nd1 > 0) { v = deg1[--nd1]; if (unmatched[v]) { break; } } // no unmatched degree 1 vertex, select the first unmatched if (v < 0 || unmatched[v]) { v = BitArrays.NextSetBit(unmatched, 0); } unmatched.Set(v, false); // find a unmatched edge and match it, adjacent degrees are updated foreach (var w in graph[v]) { if (unmatched[w]) { Match(v, w); nMatched += 2; unmatched.Set(w, false); // update neighbors of w and v (if needed) foreach (var u in graph[w]) { if (--deg[u] == 1 && unmatched[u]) { deg1[nd1++] = u; } } // if deg == 1, w is the only neighbor if (deg[v] > 1) { foreach (var u in graph[v]) { if (--deg[u] == 1 && unmatched[u]) { deg1[nd1++] = u; } } } break; } } } return(nMatched == BitArrays.Cardinality(subset)); }
/// <summary> /// Parsing of the CDKRGraph. This is the recursive method /// to perform a query. The method will recursively /// parse the CDKRGraph thru connected nodes and visiting the /// CDKRGraph using allowed adjacency relationship. /// </summary> /// <param name="traversed">node already parsed</param> /// <param name="extension">possible extension node (allowed neighbors)</param> /// <param name="forbidden">node forbidden (set of node incompatible with the current solution)</param> private void ParseRec(BitArray traversed, BitArray extension, BitArray forbidden) { BitArray newTraversed = null; BitArray newExtension = null; BitArray newForbidden = null; BitArray potentialNode = null; CheckTimeOut(); // if there is no more extension possible we // have reached a potential new solution if (BitArrays.IsEmpty(extension)) { Solution(traversed); } // carry on with each possible extension else { // calculates the set of nodes that may still // be reached at this stage (not forbidden) potentialNode = ((BitArray)GraphBitSet.Clone()); BitArrays.AndNot(potentialNode, forbidden); potentialNode.Or(traversed); // checks if we must continue the search // according to the potential node set if (MustContinue(potentialNode)) { // carry on research and update iteration count NbIteration = NbIteration + 1; // for each node in the set of possible extension (neighbors of // the current partial solution, include the node to the solution // and parse recursively the CDKRGraph with the new context. for (int x = BitArrays.NextSetBit(extension, 0); x >= 0; x = BitArrays.NextSetBit(extension, x + 1)) { #if !DEBUG && !TEST if (IsStop) { break; } #endif // evaluates the new set of forbidden nodes // by including the nodes not compatible with the // newly accepted node. newForbidden = (BitArray)forbidden.Clone(); newForbidden.Or((Graph[x]).Forbidden); // if maxIterator is the first time we are here then // traversed is empty and we initialize the set of // possible extensions to the extension of the first // accepted node in the solution. if (BitArrays.IsEmpty(traversed)) { newExtension = (BitArray)((Graph[x]).Extension.Clone()); } // else we simply update the set of solution by // including the neighbors of the newly accepted node else { newExtension = (BitArray)extension.Clone(); newExtension.Or((Graph[x]).Extension); } // extension my not contain forbidden nodes BitArrays.AndNot(newExtension, newForbidden); // create the new set of traversed node // (update current partial solution) // and add x to the set of forbidden node // (a node may only appear once in a solution) newTraversed = (BitArray)traversed.Clone(); newTraversed.Set(x, true); forbidden.Set(x, true); // parse recursively the CDKRGraph ParseRec(newTraversed, newExtension, newForbidden); } } } }
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> /// Create the topologies (stereo configurations) for the chemical graph. The /// topologies define spacial arrangement around atoms. /// </summary> private void CreateTopologies(CharBuffer buffer) { // create topologies (stereo configurations) foreach (var e in configurations) { AddTopology(e.Key, Topology.ToExplicit(g, e.Key, e.Value)); } for (int v = BitArrays.NextSetBit(checkDirectionalBonds, 0); v >= 0; v = BitArrays.NextSetBit(checkDirectionalBonds, v + 1)) { int nUpV = 0; int nDownV = 0; int nUpW = 0; int nDownW = 0; int w = -1; { int d = g.Degree(v); for (int j = 0; j < d; ++j) { Edge e = g.EdgeAt(v, j); Bond bond = e.GetBond(v); if (bond == Bond.Up) { nUpV++; } else if (bond == Bond.Down) { nDownV++; } else if (bond == Bond.Double) { w = e.Other(v); } } } if (w < 0) { continue; } BitArrays.EnsureCapacity(checkDirectionalBonds, w + 1); checkDirectionalBonds.Set(w, false); { int d = g.Degree(w); for (int j = 0; j < d; ++j) { Edge e = g.EdgeAt(w, j); Bond bond = e.GetBond(w); if (bond == Bond.Up) { nUpW++; } else if (bond == Bond.Down) { nDownW++; } } } if (nUpV + nDownV == 0 || nUpW + nDownW == 0) { continue; } if (nUpV > 1 || nDownV > 1) { int offset1 = -1, offset2 = -1; foreach (var e in g.GetEdges(v)) { if (e.Bond.IsDirectional) { if (offset1 < 0) { offset1 = bondStrPos[e]; } else { offset2 = bondStrPos[e]; } } } var errorPos = InvalidSmilesException.Display(buffer, offset1 - buffer.Length, offset2 - buffer.Length); if (strict) { throw new InvalidSmilesException($"Ignored invalid Cis/Trans specification: {errorPos}"); } else { warnings.Add($"Ignored invalid Cis/Trans specification: {errorPos}"); } } if (nUpW > 1 || nDownW > 1) { int offset1 = -1; int offset2 = -1; foreach (var e in g.GetEdges(w)) { if (e.Bond.IsDirectional) { if (offset1 < 0) { offset1 = bondStrPos[e]; } else { offset2 = bondStrPos[e]; } } } var errorPos = InvalidSmilesException.Display(buffer, offset1 - buffer.Length, offset2 - buffer.Length); if (strict) { throw new InvalidSmilesException($"Ignored invalid Cis/Trans specification: {errorPos}"); } else { warnings.Add($"Ignored invalid Cis/Trans specification: {errorPos}"); } } } }