/// <inheritdoc/> public Cycles Find(IAtomContainer molecule, int length) { var bondMap = EdgeToBondMap.WithSpaceFor(molecule); var graph = GraphUtil.ToAdjList(molecule, bondMap); var ringSearch = new RingSearch(molecule, graph); var walks = new List <int[]>(6); // all isolated cycles are relevant - all we need to do is walk around // the vertices in the subset 'isolated' foreach (var isolated in ringSearch.Isolated()) { if (isolated.Length <= length) { walks.Add(GraphUtil.Cycle(graph, isolated)); } } // each biconnected component which isn't an isolated cycle is processed // separately as a subgraph. foreach (var fused in ringSearch.Fused()) { // make a subgraph and 'apply' the cycle computation - the walk // (path) is then lifted to the original graph foreach (var cycle in Apply(GraphUtil.Subgraph(graph, fused), length)) { walks.Add(Lift(cycle, fused)); } } return(new Cycles(walks.ToArray(), molecule, bondMap)); }
/// <summary> /// Find and mark all cyclic atoms and bonds in the provided molecule. This optimised version /// allows the caller to optionally provided indexed fast access structure which would otherwise /// be created. /// </summary> /// <param name="mol">molecule</param> /// <param name="adjList"></param> /// <param name="bondMap"></param> /// <returns>Number of rings found (circuit rank)</returns> /// <seealso cref="IMolecularEntity.IsInRing"/> /// <seealso href="https://en.wikipedia.org/wiki/Circuit_rank">Circuit Rank</seealso> public static int MarkRingAtomsAndBonds(IAtomContainer mol, int[][] adjList, EdgeToBondMap bondMap) { var ringSearch = new RingSearch(mol, adjList); for (int v = 0; v < mol.Atoms.Count; v++) { mol.Atoms[v].IsInRing = false; foreach (var w in adjList[v]) { // note we only mark the bond on second visit (first v < w) and // clear flag on first visit (or if non-cyclic) if (v > w && ringSearch.Cyclic(v, w)) { bondMap[v, w].IsInRing = true; mol.Atoms[v].IsInRing = true; mol.Atoms[w].IsInRing = true; } else { bondMap[v, w].IsInRing = false; } } } return(ringSearch.NumRings); }
public virtual void TestToAdjList_withMap() { IAtomContainer container = Simple; EdgeToBondMap map = new EdgeToBondMap(); int[][] adjacent = GraphUtil.ToAdjList(container, map); Assert.AreEqual(5, adjacent.Length, "adjacency list should have 5 vertices"); Assert.AreEqual(1, adjacent[0].Length, "vertex 'a' should have degree 1"); Assert.AreEqual(3, adjacent[1].Length, "vertex 'b' should have degree 3"); Assert.AreEqual(2, adjacent[2].Length, "vertex 'c' should have degree 2"); Assert.AreEqual(1, adjacent[3].Length, "vertex 'd' should have degree 1"); Assert.AreEqual(1, adjacent[4].Length, "vertex 'e' should have degree 1"); Assert.IsTrue(Compares.AreDeepEqual(new int[] { 1 }, adjacent[0])); Assert.IsTrue(Compares.AreDeepEqual(new int[] { 0, 2, 4 }, adjacent[1])); Assert.IsTrue(Compares.AreDeepEqual(new int[] { 1, 3 }, adjacent[2])); Assert.IsTrue(Compares.AreDeepEqual(new int[] { 2 }, adjacent[3])); Assert.IsTrue(Compares.AreDeepEqual(new int[] { 1 }, adjacent[4])); Assert.IsNotNull(map[0, 1]); Assert.IsNotNull(map[1, 2]); Assert.AreSame(map[0, 1], map[1, 0]); Assert.AreSame(map[1, 2], map[2, 1]); }
/// <summary> /// Obtain the bond between the atoms at index <paramref name="u"/> and 'v'. If the 'bondMap' /// is non-null it is used for direct lookup otherwise the slower linear /// lookup in 'container' is used. /// </summary> /// <param name="container">a structure</param> /// <param name="bondMap">optimised map of atom indices to bond instances</param> /// <param name="u">an atom index</param> /// <param name="v">an atom index (connected to u)</param> /// <returns>the bond between u and v</returns> private static IBond GetBond(IAtomContainer container, EdgeToBondMap bondMap, int u, int v) { if (bondMap != null) { return(bondMap[u, v]); } return(container.GetBond(container.Atoms[u], container.Atoms[v])); }
/// <summary> /// Internal - convert a set of cycles to an ring set. /// </summary> /// <param name="container">molecule</param> /// <param name="cycles">a cycle of the chemical graph</param> /// <param name="bondMap">mapping of the edges (int,int) to the bonds of the container</param> /// <returns>the ring set</returns> private static IRingSet ToRingSet(IAtomContainer container, int[][] cycles, EdgeToBondMap bondMap) { // note currently no way to say the size of the RingSet // even through we know it var builder = container.Builder; var rings = builder.NewRingSet(); foreach (var cycle in cycles) { rings.Add(ToRing(container, cycle, bondMap)); } return(rings); }
/// <summary> /// Internal - convert a set of cycles to a ring. /// </summary> /// <param name="container">molecule</param> /// <param name="cycle">a cycle of the chemical graph</param> /// <param name="bondMap">mapping of the edges (int,int) to the bonds of the container</param> /// <returns>the ring for the specified cycle</returns> private static IRing ToRing(IAtomContainer container, int[] cycle, EdgeToBondMap bondMap) { var atoms = new IAtom[cycle.Length - 1]; var bonds = new IBond[cycle.Length - 1]; for (int i = 1; i < cycle.Length; i++) { int v = cycle[i]; int u = cycle[i - 1]; atoms[i - 1] = container.Atoms[u]; bonds[i - 1] = GetBond(container, bondMap, u, v); } var builder = container.Builder; var ring = builder.NewAtomContainer(atoms, bonds); return(builder.NewRing(ring)); }
/// <summary> /// Create an adjacent list representation of the <paramref name="container"/> and /// fill in the <paramref name="bondMap"/> for quick lookup. /// </summary> /// <param name="container">the molecule</param> /// <param name="bondMap">a map to index the bonds into</param> /// <returns>adjacency list representation stored as an <see cref="int"/>[][].</returns> /// <exception cref="ArgumentNullException">the container was null</exception> /// <exception cref="ArgumentException">a bond was found which contained atoms not in the molecule</exception> public static int[][] ToAdjList(IAtomContainer container, EdgeToBondMap bondMap) { if (container == null) { throw new ArgumentNullException(nameof(container)); } int n = container.Atoms.Count; var graph = new List <int> [n]; for (var i = 0; i < n; i++) { graph[i] = new List <int>(); } foreach (var bond in container.Bonds) { int v = container.Atoms.IndexOf(bond.Begin); int w = container.Atoms.IndexOf(bond.End); if (v < 0 || w < 0) { throw new ArgumentException($"bond at index {container.Bonds.IndexOf(bond)} contained an atom not present in molecule"); } graph[v].Add(w); graph[w].Add(v); bondMap?.Add(v, w, bond); } var agraph = new int[n][]; for (int v = 0; v < n; v++) { agraph[v] = graph[v].ToArray(); } return(agraph); }
/// <summary> /// Internal constructor - may change in future but currently just takes the /// cycle paths and the container from which they came. /// </summary> /// <param name="paths">the cycle paths (closed vertex walks)</param> /// <param name="container">the input container</param> /// <param name="bondMap"></param> private Cycles(int[][] paths, IAtomContainer container, EdgeToBondMap bondMap) { this.paths = paths; this.container = container; this.bondMap = bondMap; }
/// <summary> /// Find and mark all cyclic atoms and bonds in the provided molecule. /// </summary> /// <param name="mol">molecule</param> /// <returns>Number of rings found (circuit rank)</returns> /// <seealso cref="IMolecularEntity.IsInRing"/> /// <seealso href="https://en.wikipedia.org/wiki/Circuit_rank">Circuit Rank</seealso> public static int MarkRingAtomsAndBonds(IAtomContainer mol) { var bonds = EdgeToBondMap.WithSpaceFor(mol); return(MarkRingAtomsAndBonds(mol, GraphUtil.ToAdjList(mol, bonds), bonds)); }