Exemplo n.º 1
0
        /// <summary>
        /// Second traversal writes the bonds and atoms to the SMILES string.
        /// </summary>
        /// <param name="u">a vertex</param>
        /// <param name="p">previous vertex</param>
        /// <param name="b">the bond from the previous vertex to this vertex</param>
        public void Write(int u, int p, Bond b)
        {
            visitedAt[u] = nVisit++;

            int remaining = g.Degree(u);

            if (u != p)
            {
                remaining--;
            }

            // assign ring numbers
            if (rings.TryGetValue(u, out IList <RingClosure> closures))
            {
                foreach (var rc in closures)
                {
                    // as we are composing tokens, make sure apply in reverse
                    int rnum = rnums.Next();
                    if (rc.Register(rnum))
                    {
                        int v = rc.Other(u);
                        tokens[u] = new RingNumberToken(new RingBondToken(tokens[u],
                                                                          rc.GetBond(u)),
                                                        rnum);
                        rnums.Use(rnum);
                    }
                    else
                    {
                        tokens[u] = new RingNumberToken(tokens[u],
                                                        rc.RNum);
                        rnums.Free(rc.RNum);
                    }
                    remaining--;
                }
            }

            sb.Append(b.Token);
            tokens[u].Append(sb);

            int d = g.Degree(u);

            for (int j = 0; j < d; ++j)
            {
                Edge e = g.EdgeAt(u, j);
                int  v = e.Other(u);
                if (visitedAt[v] < 0)
                {
                    if (--remaining > 0)
                    {
                        sb.Append('(');
                        Write(v, u, e.GetBond(u));
                        sb.Append(')');
                    }
                    else
                    {
                        Write(v, u, e.GetBond(u));
                    }
                }
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// First traversal of the molecule assigns ring bonds (numbered later) and
        /// configures topologies.
        /// </summary>
        /// <param name="u">the vertex to visit</param>
        /// <param name="p">the atom we came from</param>
        void Prepare(int u, int p)
        {
            visitedAt[u]    = nVisit++;
            tokens[u]       = g.GetAtom(u).Token;
            tokens[u].Graph = g;
            tokens[u].Index = u;

            int d = g.Degree(u);

            for (int j = 0; j < d; ++j)
            {
                Edge e = g.EdgeAt(u, j);
                int  v = e.Other(u);
                if (visitedAt[v] < 0)
                {
                    Prepare(v, u);
                }
                else if (v != p && visitedAt[v] < visitedAt[u])
                {
                    CyclicEdge(v, u, e.GetBond(v));
                }
            }

            PrepareStereochemistry(u, p);
        }
Exemplo n.º 3
0
        private Bond FirstDirectionalLabel(int u, int x, BitArray adjToDb)
        {
            Edge e = g.CreateEdge(u, x);
            Bond b = e.GetBond(u);

            // the edge is next to another double bond configuration, we
            // need to consider its assignment
            if (adjToDb[x] && g.Degree(x) > 2)
            {
                foreach (var f in g.GetEdges(x))
                {
                    if (f.Other(x) != u && f.Bond != Bond.Double && f.Bond.IsDirectional)
                    {
                        return(f.GetBond(x));
                    }
                }
            }
            // consider other labels on this double-bond
            if (g.Degree(u) > 2)
            {
                foreach (var f in g.GetEdges(u))
                {
                    if (f.Other(u) != x && f.Bond != Bond.Double && f.Bond.IsDirectional)
                    {
                        return(f.GetBond(u).Inverse());
                    }
                }
            }
            return(b.IsDirectional ? b : Bond.Down);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Given a double bond edge traverse the neighbors of one of the endpoints
        /// and accumulate any explicit replacements in the <paramref name="acc"/> accumulator.
        /// </summary>
        /// <param name="g">the chemical graph</param>
        /// <param name="e">a edge in the graph ('double bond type')</param>
        /// <param name="u">a endpoint of the edge <paramref name="e"/></param>
        /// <param name="acc">accumulator for new edges</param>
        /// <returns>does the edge <paramref name="e"/> need to be reconsidered later</returns>
        /// <exception cref="InvalidSmilesException">thrown if the edge could not be converted</exception>
        private bool ReplaceImplWithExpl(Graph g,
                                         Edge e,
                                         int u,
                                         Dictionary <Edge, Edge> acc)
        {
            Edge implicit_ = null;
            Edge explicit_ = null;

            foreach (var f in g.GetEdges(u))
            {
                Edge f2 = acc.ContainsKey(f) ? acc[f] : f;
                var  aa = f2.GetBond(u);
                if (aa == Bond.Single || aa == Bond.Implicit)
                {
                    if (implicit_ != null)
                    {
                        return(true);
                    }
                    implicit_ = f;
                }
                else if (aa == Bond.Double)
                {
                    if (!f.Equals(e))
                    {
                        return(false);
                    }
                }
                else if (aa == Bond.Up || aa == Bond.Down)
                {
                    if (explicit_ != null)
                    {
                        if (acc.ContainsKey(explicit_))
                        {
                            explicit_ = acc[explicit_];
                        }

                        // original bonds are invalid
                        if ((f.Bond == Bond.Up || f.Bond == Bond.Down) &&
                            explicit_.GetBond(u).Inverse() != f.GetBond(u))
                        {
                            throw new InvalidSmilesException("invalid double bond configuration");
                        }

                        if (explicit_.GetBond(u).Inverse() != f2.GetBond(u))
                        {
                            acc[f] = f2.Inverse();
                            BitArray visited = new BitArray(g.Order);
                            visited.Set(u, true);
                            InvertExistingDirectionalLabels(g, visited, acc, f2
                                                            .Other(u));
                        }
                        return(false);
                    }
                    explicit_ = f;
                }
            }

            // no implicit or explicit bond? don't do anything
            if (implicit_ == null || explicit_ == null)
            {
                return(false);
            }

            if (acc.ContainsKey(explicit_))
            {
                explicit_ = acc[explicit_];
            }

            int v = implicit_.Other(u);

            if (!acc.TryGetValue(implicit_, out Edge existing))
            {
                existing = null;
            }
            acc[implicit_] = new Edge(u,
                                      v,
                                      explicit_.GetBond(u)
                                      .Inverse());

            if (existing != null && existing.GetBond(u) != explicit_.GetBond(u).Inverse())
            {
                throw new InvalidSmilesException("unable to assign explicit type for " + implicit_);
            }

            return(false);
        }
Exemplo n.º 5
0
        /// <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);
                }
            }
        }
Exemplo n.º 6
0
        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!
            }
        }