Esempio n. 1
0
            /// <inheritdoc/>
            public Cycles Find(IAtomContainer molecule, int[][] graph, int length)
            {
                RingSearch ringSearch = new RingSearch(molecule, graph);

                if (this.predefinedLength < length)
                {
                    length = this.predefinedLength;
                }

                IList <int[]> 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 FindInFused(GraphUtil.Subgraph(graph, fused), length))
                    {
                        walks.Add(Lift(cycle, fused));
                    }
                }

                return(new Cycles(walks.ToArray(), molecule, null));
            }
Esempio n. 2
0
        /// <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);
        }
Esempio n. 3
0
            /// <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>
        /// Adjust all double bond elements in the provided structure.
        /// </summary>
        /// <param name="container">the structure to adjust</param>
        /// <param name="graph">the adjacency list representation of the structure</param>
        /// <exception cref="ArgumentException">an atom had unset coordinates</exception>
        CorrectGeometricConfiguration(IAtomContainer container, int[][] graph)
        {
            this.container   = container;
            this.graph       = graph;
            this.visited     = new bool[graph.Length];
            this.atomToIndex = new Dictionary <IAtom, int>();
            this.ringSearch  = new RingSearch(container, graph);

            for (int i = 0; i < container.Atoms.Count; i++)
            {
                IAtom atom = container.Atoms[i];
                atomToIndex[atom] = i;
                if (atom.Point2D == null)
                {
                    throw new ArgumentException("atom " + i + " had unset coordinates");
                }
            }

            foreach (var element in container.StereoElements)
            {
                if (element is IDoubleBondStereochemistry)
                {
                    Adjust((IDoubleBondStereochemistry)element);
                }
                else if (element is ExtendedCisTrans)
                {
                    Adjust((ExtendedCisTrans)element);
                }
            }
        }
Esempio n. 5
0
        /// <inheritdoc/>
        public override IReadOnlyList <int> Contribution(IAtomContainer container, RingSearch ringSearch)
        {
            int n         = container.Atoms.Count;
            var electrons = new int[n];
            var piBonds   = new int[n];

            // count number of cyclic pi bonds
            foreach (var bond in container.Bonds)
            {
                int u = container.Atoms.IndexOf(bond.Begin);
                int v = container.Atoms.IndexOf(bond.End);

                if (bond.Order == BondOrder.Double && ringSearch.Cyclic(u, v))
                {
                    piBonds[u]++;
                    piBonds[v]++;
                }
            }

            // any atom which is adjacent to one (and only one) cyclic
            // pi bond contributes 1 electron
            for (int i = 0; i < n; i++)
            {
                electrons[i] = piBonds[i] == 1 ? 1 : -1;
            }

            return(electrons);
        }
Esempio n. 6
0
        /// <summary>
        /// Find the bonds of a <paramref name="molecule"/> which this model determined were aromatic.
        /// </summary>
        /// <example>
        /// <include file='IncludeExamples.xml' path='Comments/Codes[@id="NCDK.Aromaticities.Aromaticity_Example.cs+FindBonds"]/*' />
        /// </example>
        /// <param name="molecule">the molecule to apply the model to</param>
        /// <returns>the set of bonds which are aromatic</returns>
        /// <exception cref="CDKException">a problem occurred with the cycle perception - one can retry with a simpler cycle set</exception>
        public IEnumerable <IBond> FindBonds(IAtomContainer molecule)
        {
            // build graph data-structures for fast cycle perception
            var bondMap = EdgeToBondMap.WithSpaceFor(molecule);
            var graph   = GraphUtil.ToAdjList(molecule, bondMap);

            // initial ring/cycle search and get the contribution from each atom
            RingSearch ringSearch = new RingSearch(molecule, graph);
            var        electrons  = model.Contribution(molecule, ringSearch);

            // obtain the subset of electron contributions which are >= 0 (i.e.
            // allowed to be aromatic) - we then find the cycles in this subgraph
            // and 'lift' the indices back to the original graph using the subset
            // as a lookup
            var subset   = Subset(electrons);
            var subgraph = GraphUtil.Subgraph(graph, subset);

            // for each cycle if the electron sum is valid add the bonds of the
            // cycle to the set or aromatic bonds
            foreach (var cycle in cycles.Find(molecule, subgraph, subgraph.Length).GetPaths())
            {
                if (CheckElectronSum(cycle, electrons, subset))
                {
                    for (int i = 1; i < cycle.Length; i++)
                    {
                        yield return(bondMap[subset[cycle[i]], subset[cycle[i - 1]]]);
                    }
                }
            }
            yield break;
        }
Esempio n. 7
0
        public static bool IsArene(this IAtomContainer mol)
        {
            if (mol == null)
            {
                return(false);
            }

            var ringSearch = new RingSearch(mol);

            //this is null for isolated and fused rings
            var cyclic = ringSearch.Cyclic();

            if (cyclic == null)
            {
                return(false);
            }

            for (var i = 1; i < mol.Bonds.Count; i++)
            {
                var p0 = mol.Bonds[i - 1].Order == BondOrder.Single ^ mol.Bonds[i].Order == BondOrder.Single;
                var p1 = mol.Bonds[i - 1].Order == BondOrder.Double ^ mol.Bonds[i].Order == BondOrder.Double;
                if (p0 && p1)
                {
                    continue;
                }

                return(false);
            }

            return(true);
        }
Esempio n. 8
0
        /// <summary>
        /// Recognise the cyclic carbohydrate projections.
        /// </summary>
        /// <param name="projections">the types of projections to recognise</param>
        /// <returns>recognised stereocenters</returns>
        public IEnumerable <IStereoElement <IChemObject, IChemObject> > Recognise(ICollection <Projection> projections)
        {
            if (!projections.Contains(Projection.Haworth) && !projections.Contains(Projection.Chair))
            {
                yield break;
            }

            var ringSearch = new RingSearch(container, graph);

            foreach (var isolated in ringSearch.Isolated())
            {
                if (isolated.Length < 5 || isolated.Length > 7)
                {
                    continue;
                }

                var cycle = Arrays.CopyOf(GraphUtil.Cycle(graph, isolated),
                                          isolated.Length);

                var points     = CoordinatesOfCycle(cycle, container);
                var turns      = GetTurns(points);
                var projection = WoundProjection.OfTurns(turns);

                if (!projections.Contains(projection.Projection))
                {
                    continue;
                }

                // ring is not aligned correctly for Haworth
                if (projection.Projection == Projection.Haworth && !CheckHaworthAlignment(points))
                {
                    continue;
                }

                var horizontalXy = HorizontalOffset(points, turns, projection.Projection);

                // near vertical, should also flag as potentially ambiguous
                if (1 - Math.Abs(horizontalXy.Y) < QuartCardinalityThreshold)
                {
                    continue;
                }

                var above = (int[])cycle.Clone();
                var below = (int[])cycle.Clone();

                if (!AssignSubstituents(cycle, above, below, projection, horizontalXy))
                {
                    continue;
                }

                foreach (var center in NewTetrahedralCenters(cycle, above, below, projection))
                {
                    yield return(center);
                }
            }

            yield break;
        }
Esempio n. 9
0
 /// <summary>
 /// Create a perception method for the provided container, graph
 /// representation and bond map.
 /// </summary>
 /// <param name="container">native CDK structure representation</param>
 /// <param name="graph">graph representation (adjacency list)</param>
 /// <param name="bondMap">fast lookup bonds by atom index</param>
 internal Stereocenters(IAtomContainer container, int[][] graph, EdgeToBondMap bondMap)
 {
     this.container         = container;
     this.bondMap           = bondMap;
     this.g                 = graph;
     this.ringSearch        = new RingSearch(container, graph);
     this.elements          = new StereoElement[g.Length];
     this.stereocenters     = new CenterType[g.Length];
     this.numStereoElements = CreateElements();
 }
Esempio n. 10
0
        public override ISet <int> Find(long[] invariants, IAtomContainer container, int[][] graph)
        {
            int n = invariants.Length;

            // find cyclic vertices using DFS
            RingSearch ringSearch = new RingSearch(container, graph);

            // ordered map of the set of vertices for each value
            var equivalent = new SortedDictionary <long, ISet <int> >();

            // divide the invariants into equivalent indexed and ordered sets
            for (int i = 0; i < invariants.Length; i++)
            {
                long invariant = invariants[i];
                if (!equivalent.TryGetValue(invariant, out ISet <int> set))
                {
                    if (ringSearch.Cyclic(i))
                    {
                        set = new HashSet <int>
                        {
                            i
                        };
                        equivalent[invariant] = set;
                    }
                }
                else
                {
                    set.Add(i);
                }
            }

            // find the smallest set of equivalent cyclic vertices
            int        minSize = int.MaxValue;
            ISet <int> min     = new SortedSet <int>();

            foreach (var e in equivalent)
            {
                ISet <int> vertices = e.Value;
                if (vertices.Count < minSize && vertices.Count > 1)
                {
                    min     = vertices;
                    minSize = vertices.Count;
                }
                else if (vertices.Count == minSize)
                {
                    foreach (var vertice in vertices)
                    {
                        min.Add(vertice);
                    }
                }
            }

            return(min);
        }
Esempio n. 11
0
        void Main()
        {
            {
                #region
                // using NCDK.Graphs.GraphUtil;
                IAtomContainer m = TestMoleculeFactory.MakeAnthracene();

                // compute on the whole graph
                RelevantCycles relevant = new RelevantCycles(ToAdjList(m));

                // it is much faster to compute on the separate ring systems of the molecule
                int[][]    graph      = ToAdjList(m);
                RingSearch ringSearch = new RingSearch(m, graph);

                // all isolated cycles are relevant
                foreach (int[] isolated in ringSearch.Isolated())
                {
                    int[] path = Cycle(graph, isolated);
                }

                // compute the relevant cycles for each system
                foreach (int[] fused in ringSearch.Fused())
                {
                    int[][]        subgraph           = Subgraph(graph, fused);
                    RelevantCycles relevantOfSubgraph = new RelevantCycles(subgraph);

                    foreach (int[] path in relevantOfSubgraph.GetPaths())
                    {
                        // convert the sub graph vertices back to the super graph indices
                        for (int i = 0; i < path.Length; i++)
                        {
                            path[i] = fused[path[i]];
                        }
                    }
                }
                #endregion
            }
            {
                IAtomContainer mol = null;
                #region GetPaths
                RelevantCycles relevant = new RelevantCycles(ToAdjList(mol));

                // ensure the number is manageable
                if (relevant.Count() < 100)
                {
                    foreach (int[] path in relevant.GetPaths())
                    {
                        // process the path
                    }
                }
                #endregion
            }
        }
Esempio n. 12
0
        public static void Main(string[] args)
        {
            // convert the molecule to adjacency list - may be redundant in future
            IAtomContainer m = TestMoleculeFactory.MakeAlphaPinene();

            int[][] g = GraphUtil.ToAdjList(m);

            // efficient computation/partitioning of the ring systems
            RingSearch rs = new RingSearch(m, g);

            // isolated cycles don't need to be run
            rs.Isolated();

            // process fused systems separately
            foreach (var fused in rs.Fused())
            {
                const int maxDegree = 100;
                // given the fused subgraph, max cycle size is
                // the number of vertices
                AllCycles ac = new AllCycles(GraphUtil.Subgraph(g, fused), fused.Length, maxDegree);
                // cyclic walks
                int[][] paths = ac.GetPaths();
            }
        }
Esempio n. 13
0
        /// <inheritdoc/>
        public override IReadOnlyList <int> Contribution(IAtomContainer container, RingSearch ringSearch)
        {
            var nAtoms    = container.Atoms.Count;
            var electrons = new int[nAtoms];

            Arrays.Fill(electrons, -1);

            var indexMap = new Dictionary <IAtom, int>();

            for (int i = 0; i < nAtoms; i++)
            {
                var atom = container.Atoms[i];
                indexMap.Add(atom, i);

                // acyclic atom skipped
                if (!ringSearch.Cyclic(i))
                {
                    continue;
                }

                var hyb = atom.Hybridization;

                CheckNotNull(atom.AtomTypeName, "atom has unset atom type");

                // atom has been assigned an atom type but we don't know the hybrid state,
                // typically for atom type 'X' (unknown)
                switch (hyb)
                {
                case Hybridization.SP2:
                case Hybridization.Planar3:
                    electrons[i] = ElectronsForAtomType(atom);
                    break;

                case Hybridization.SP3:
                    electrons[i] = LonePairCount(atom) > 0 ? 2 : -1;
                    break;
                }
            }

            // exocyclic double bonds are allowed no further processing
            if (exocyclic)
            {
                return(electrons);
            }

            // check for exocyclic double/triple bonds and disallow their contribution
            foreach (var bond in container.Bonds)
            {
                if (bond.Order == BondOrder.Double || bond.Order == BondOrder.Triple)
                {
                    var a1 = bond.Begin;
                    var a2 = bond.End;

                    var a1Type = a1.AtomTypeName;
                    var a2Type = a2.AtomTypeName;

                    var u = indexMap[a1];
                    var v = indexMap[a2];

                    if (!ringSearch.Cyclic(u, v))
                    {
                        // XXX: single exception - we could make this more general but
                        // for now this mirrors the existing behavior
                        switch (a1Type)
                        {
                        case "N.sp2.3":
                            switch (a2Type)
                            {
                            case "O.sp2":
                                continue;
                            }
                            break;

                        case "O.sp2":
                            switch (a2Type)
                            {
                            case "N.sp2.3":
                                continue;
                            }
                            break;
                        }

                        electrons[u] = electrons[v] = -1;
                    }
                }
            }

            return(electrons);
        }
Esempio n. 14
0
        /// <summary>
        /// Recognise the tetrahedral stereochemistry in the provided structure.
        /// </summary>
        /// <param name="projections">allowed projection types</param>
        /// <returns>zero of more stereo elements</returns>
        public IEnumerable <IStereoElement <IChemObject, IChemObject> > Recognise(ICollection <Projection> projections)
        {
            if (!projections.Contains(Projection.Fischer))
            {
                return(Array.Empty <IStereoElement <IChemObject, IChemObject> >());
            }

            // build atom index and only recognize 2D depictions
            var atomToIndex = new Dictionary <IAtom, int>();

            foreach (var atom in container.Atoms)
            {
                if (atom.Point2D == null)
                {
                    return(Array.Empty <IStereoElement <IChemObject, IChemObject> >());
                }
                atomToIndex.Add(atom, atomToIndex.Count);
            }

            var ringSearch = new RingSearch(container, graph);

            var elements = new List <IStereoElement <IChemObject, IChemObject> >(5);

            for (int v = 0; v < container.Atoms.Count; v++)
            {
                var focus = container.Atoms[v];
                if (!focus.AtomicNumber.Equals(AtomicNumbers.Carbon))
                {
                    continue;
                }
                if (ringSearch.Cyclic(v))
                {
                    continue;
                }
                if (stereocenters.ElementType(v) != CoordinateType.Tetracoordinate)
                {
                    continue;
                }
                if (!stereocenters.IsStereocenter(v))
                {
                    continue;
                }

                var element = NewTetrahedralCenter(focus, Neighbors(v, graph, bonds));

                if (element == null)
                {
                    continue;
                }

                // east/west bonds must be to terminal atoms
                var east = element.Ligands[EAST];
                var west = element.Ligands[WEST];

                if (east != focus && !IsTerminal(east, atomToIndex))
                {
                    continue;
                }
                if (west != focus && !IsTerminal(west, atomToIndex))
                {
                    continue;
                }

                elements.Add(element);
            }

            return(elements);
        }
Esempio n. 15
0
 /// <summary>
 /// Determine the number 'p' electron contributed by each atom in the
 /// provided <paramref name="container"/>. A value of '0' indicates the atom can
 /// contribute but that it contributes no electrons. A value of '-1'
 /// indicates the atom should not contribute at all.
 /// </summary>
 /// <param name="container">molecule</param>
 /// <param name="ringSearch">ring information</param>
 /// <returns>electron contribution of each atom (-1=none)</returns>
 public abstract IReadOnlyList <int> Contribution(IAtomContainer container, RingSearch ringSearch);
Esempio n. 16
0
        /// <inheritdoc/>
        public override IReadOnlyList <int> Contribution(IAtomContainer container, RingSearch ringSearch)
        {
            int n = container.Atoms.Count;

            // we compute values we need for all atoms and then make the decisions
            // - this avoids costly operations such as looking up connected
            // bonds on each atom at the cost of memory
            var degree          = new int[n];
            var bondOrderSum    = new int[n];
            var nCyclicPiBonds  = new int[n];
            var exocyclicPiBond = new int[n];
            var electrons       = new int[n];

            Arrays.Fill(exocyclicPiBond, -1);

            // index atoms and set the degree to the number of implicit hydrogens
            var atomIndex = new Dictionary <IAtom, int>(n);

            for (int i = 0; i < n; i++)
            {
                var a = container.Atoms[i];
                atomIndex.Add(a, i);
                degree[i] = CheckNotNull(a.ImplicitHydrogenCount,
                                         "Aromaticity model requires implicit hydrogen count is set.");
            }

            // for each bond we increase the degree count and check for cyclic and
            // exocyclic pi bonds. if there is a cyclic pi bond the atom is marked.
            // if there is an exocyclic pi bond we store the adjacent atom for
            // lookup later.
            foreach (var bond in container.Bonds)
            {
                var u = atomIndex[bond.Begin];
                var v = atomIndex[bond.End];
                degree[u]++;
                degree[v]++;

                var order = CheckNotNull(bond.Order, "Aromaticity model requires that bond orders must be set");

                if (order == BondOrder.Unset)
                {
                    throw new ArgumentException("Aromaticity model requires that bond orders must be set");
                }
                else if (order == BondOrder.Double)
                {
                    if (ringSearch.Cyclic(u, v))
                    {
                        nCyclicPiBonds[u]++;
                        nCyclicPiBonds[v]++;
                    }
                    else
                    {
                        exocyclicPiBond[u] = v;
                        exocyclicPiBond[v] = u;
                    }
                    // note - fall through
                }

                if (order == BondOrder.Single ||
                    order == BondOrder.Double ||
                    order == BondOrder.Triple ||
                    order == BondOrder.Quadruple)
                {
                    bondOrderSum[u] += order.Numeric();
                    bondOrderSum[v] += order.Numeric();
                }
            }

            // now make a decision on how many electrons each atom contributes
            for (int i = 0; i < n; i++)
            {
                var element = Element(container.Atoms[i]);
                var charge  = Charge(container.Atoms[i]);

                // abnormal valence, usually indicated a radical. these cause problems
                // with kekulisations
                var bondedValence = bondOrderSum[i] + container.Atoms[i].ImplicitHydrogenCount.Value;
                if (!Normal(element, charge, bondedValence))
                {
                    electrons[i] = -1;
                }

                // non-aromatic element, acyclic atoms, atoms with more than three
                // neighbors and atoms with more than 1 cyclic pi bond are not
                // considered
                else if (!AromaticElement(element) || !ringSearch.Cyclic(i) || degree[i] > 3 || nCyclicPiBonds[i] > 1)
                {
                    electrons[i] = -1;
                }

                // exocyclic bond contributes 0 or 1 electrons depending on
                // preset electronegativity - check the exocyclicContribution method
                else if (exocyclicPiBond[i] >= 0)
                {
                    electrons[i] = ExocyclicContribution(element, Element(container.Atoms[exocyclicPiBond[i]]), charge,
                                                         nCyclicPiBonds[i]);
                }

                // any atom (except arsenic) with one cyclic pi bond contributes a
                // single electron
                else if (nCyclicPiBonds[i] == 1)
                {
                    electrons[i] = element == ARSENIC ? -1 : 1;
                }

                // a anion with a lone pair contributes 2 electrons - simplification
                // here is we count the number free valence electrons but also
                // check if the bonded valence is okay (i.e. not a radical)
                else if (charge <= 0 && charge > -3)
                {
                    if (Valence(element, charge) - bondOrderSum[i] >= 2)
                    {
                        electrons[i] = 2;
                    }
                    else
                    {
                        electrons[i] = -1;
                    }
                }

                else
                {
                    // cation with no double bonds - single exception?
                    if (element == Carbon && charge > 0)
                    {
                        electrons[i] = 0;
                    }
                    else
                    {
                        electrons[i] = -1;
                    }
                }
            }

            return(electrons);
        }