Exemple #1
0
        /// <summary>
        /// Read a molecule from the character buffer.
        /// </summary>
        /// <param name="buffer">a character buffer</param>
        /// <exception cref="InvalidSmilesException">invalid grammar</exception>
        private void ReadSmiles(CharBuffer buffer)
        {
            // primary dispatch
            while (buffer.HasRemaining())
            {
                char c = buffer.Get();
                switch (c)
                {
                // aliphatic subset
                case '*':
                    AddAtom(AtomImpl.AliphaticSubset.Unknown, buffer);
                    break;

                case 'B':
                    if (buffer.GetIf('r'))
                    {
                        AddAtom(AtomImpl.AliphaticSubset.Bromine, buffer);
                    }
                    else
                    {
                        AddAtom(AtomImpl.AliphaticSubset.Boron, buffer);
                    }
                    break;

                case 'C':
                    if (buffer.GetIf('l'))
                    {
                        AddAtom(AtomImpl.AliphaticSubset.Chlorine, buffer);
                    }
                    else
                    {
                        AddAtom(AtomImpl.AliphaticSubset.Carbon, buffer);
                    }
                    break;

                case 'N':
                    AddAtom(AtomImpl.AliphaticSubset.Nitrogen, buffer);
                    break;

                case 'O':
                    AddAtom(AtomImpl.AliphaticSubset.Oxygen, buffer);
                    break;

                case 'P':
                    AddAtom(AtomImpl.AliphaticSubset.Phosphorus, buffer);
                    break;

                case 'S':
                    AddAtom(AtomImpl.AliphaticSubset.Sulfur, buffer);
                    break;

                case 'F':
                    AddAtom(AtomImpl.AliphaticSubset.Fluorine, buffer);
                    break;

                case 'I':
                    AddAtom(AtomImpl.AliphaticSubset.Iodine, buffer);
                    break;

                // aromatic subset
                case 'b':
                    AddAtom(AtomImpl.AromaticSubset.Boron, buffer);
                    g.AddFlags(Graph.HAS_AROM);
                    break;

                case 'c':
                    AddAtom(AtomImpl.AromaticSubset.Carbon, buffer);
                    g.AddFlags(Graph.HAS_AROM);
                    break;

                case 'n':
                    AddAtom(AtomImpl.AromaticSubset.Nitrogen, buffer);
                    g.AddFlags(Graph.HAS_AROM);
                    break;

                case 'o':
                    AddAtom(AtomImpl.AromaticSubset.Oxygen, buffer);
                    g.AddFlags(Graph.HAS_AROM);
                    break;

                case 'p':
                    AddAtom(AtomImpl.AromaticSubset.Phosphorus, buffer);
                    g.AddFlags(Graph.HAS_AROM);
                    break;

                case 's':
                    AddAtom(AtomImpl.AromaticSubset.Sulfur, buffer);
                    g.AddFlags(Graph.HAS_AROM);
                    break;

                // D/T for hydrogen isotopes - non-standard but OpenSMILES spec
                // says it's possible. The D and T here are automatic converted
                // to [2H] and [3H].
                case 'H':
                    if (strict)
                    {
                        throw new InvalidSmilesException("hydrogens should be specified in square brackets - '[H]'",
                                                         buffer);
                    }
                    AddAtom(AtomImpl.EXPLICIT_HYDROGEN, buffer);
                    break;

                case 'D':
                    if (strict)
                    {
                        throw new InvalidSmilesException("deuterium should be specified as a hydrogen isotope - '[2H]'",
                                                         buffer);
                    }
                    AddAtom(AtomImpl.DEUTERIUM, buffer);
                    break;

                case 'T':
                    if (strict)
                    {
                        throw new InvalidSmilesException("tritium should be specified as a hydrogen isotope - '[3H]'",
                                                         buffer);
                    }
                    AddAtom(AtomImpl.TRITIUM, buffer);
                    break;

                // bracket atom
                case '[':
                    AddAtom(ReadBracketAtom(buffer), buffer);
                    break;

                // ring bonds
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                    Ring(c - '0', buffer);
                    break;

                case '%':
                    int num = buffer.GetNumber(2);
                    if (num < 0)
                    {
                        throw new InvalidSmilesException("a number (<digit>+) must follow '%':", buffer);
                    }
                    if (strict && num < 10)
                    {
                        throw new InvalidSmilesException("two digits must follow '%'", buffer);
                    }
                    Ring(num, buffer);
                    break;

                // bond/dot
                case '-':
                    if (bond != Bond.Implicit)
                    {
                        throw new InvalidSmilesException("Multiple bonds specified:", buffer);
                    }
                    bond = Bond.Single;
                    break;

                case '=':
                    if (bond != Bond.Implicit)
                    {
                        throw new InvalidSmilesException("Multiple bonds specified:", buffer);
                    }
                    bond = Bond.Double;
                    break;

                case '#':
                    if (bond != Bond.Implicit)
                    {
                        throw new InvalidSmilesException("Multiple bonds specified:", buffer);
                    }
                    bond = Bond.Triple;
                    break;

                case '$':
                    if (bond != Bond.Implicit)
                    {
                        throw new InvalidSmilesException("Multiple bonds specified:", buffer);
                    }
                    bond = Bond.Quadruple;
                    break;

                case ':':
                    if (bond != Bond.Implicit)
                    {
                        throw new InvalidSmilesException("Multiple bonds specified:", buffer);
                    }
                    g.AddFlags(Graph.HAS_AROM);
                    bond = Bond.Aromatic;
                    break;

                case '/':
                    if (bond != Bond.Implicit)
                    {
                        throw new InvalidSmilesException("Multiple bonds specified:", buffer);
                    }
                    bond = Bond.Up;
                    g.AddFlags(Graph.HAS_BND_STRO);
                    break;

                case '\\':
                    // we allow C\\C=C/C since it could be an escaping error
                    if (bond != Bond.Implicit && bond != Bond.Down)
                    {
                        throw new InvalidSmilesException("Multiple bonds specified:", buffer);
                    }
                    bond = Bond.Down;
                    g.AddFlags(Graph.HAS_BND_STRO);
                    break;

                case '.':
                    if (bond != Bond.Implicit)
                    {
                        throw new InvalidSmilesException("Bond specified before disconnection:", buffer);
                    }
                    bond = Bond.Dot;
                    break;

                // branching
                case '(':
                    if (stack.IsEmpty)
                    {
                        throw new InvalidSmilesException("Cannot open branch at this position, SMILES may be truncated:", buffer);
                    }
                    stack.Push(stack.Peek());
                    break;

                case ')':
                    if (stack.Count < 2)
                    {
                        throw new InvalidSmilesException("Closing of an unopened branch, SMILES may be truncated:", buffer);
                    }
                    stack.Pop();
                    break;

                // termination
                case '\t':
                case ' ':
                    // String suffix is title
                    var sb = new StringBuilder();
                    while (buffer.HasRemaining())
                    {
                        c = buffer.Get();
                        if (c == '\n' || c == '\r')
                        {
                            break;
                        }
                        sb.Append(c);
                    }
                    g.Title = sb.ToString();
                    return;

                case '\n':
                case '\r':
                    return;

                default:
                    throw new InvalidSmilesException("unexpected character:", buffer);
                }
            }
        }
Exemple #2
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);
                }
            }
        }
Exemple #3
0
 public RingBond(int u, Bond bond)
 {
     this.u    = u;
     this.bond = bond;
 }
Exemple #4
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!
            }
        }
Exemple #5
0
 /// <summary>
 /// Connect the vertices <paramref name="u"/> and <paramref name="v"/> with the specified bond label.
 /// </summary>
 /// <param name="u">a vertex</param>
 /// <param name="v">another vertex</param>
 /// <param name="b">bond</param>
 /// <returns>graph builder for adding more atoms/connections</returns>
 public GraphBuilder Add(int u, int v, Bond b)
 {
     Add(b.CreateEdge(u, v));
     return(this);
 }
Exemple #6
0
 /// <summary>
 /// Set the bond label.
 /// </summary>
 /// <param name="bond">the bond label</param>
 public void SetBond(Bond bond)
 {
     this.Bond = bond;
 }