public void Multicenter()
        {
            var state = new CxSmilesState {
                positionVar = new SortedDictionary <int, IList <int> >()
            };

            state.positionVar[0] = new[] { 4, 5, 6, 7 };
            state.positionVar[2] = new[] { 4, 6, 5, 7 };
            Assert.AreEqual(" |m:5:0.1.2.3,7:0.1.2.3|", CxSmilesGenerator.Generate(state, SmiFlavors.CxMulticenter, Array.Empty <int>(), new int[] { 7, 6, 5, 4, 3, 2, 1, 0 }));
        }
        public void Sgroups()
        {
            CxSmilesState state = new CxSmilesState
            {
                sgroups = new List <CxSmilesState.PolymerSgroup>(1)
                {
                    new CxSmilesState.PolymerSgroup("n", new[] { 2, 3 }, "n", "ht"),
                    new CxSmilesState.PolymerSgroup("n", new[] { 5 }, "m", "ht")
                }
            };

            Assert.AreEqual(" |Sg:n:2:m:ht,Sg:n:4,5:n:ht|", CxSmilesGenerator.Generate(state, SmiFlavors.CxPolymer, Array.Empty <int>(), new int[] { 7, 6, 5, 4, 3, 2, 1, 0 }));
        }
        public void Radicals()
        {
            CxSmilesState state = new CxSmilesState
            {
                atomRads = new SortedDictionary <int, CxSmilesState.Radical>
                {
                    [2] = CxSmilesState.Radical.Monovalent,
                    [6] = CxSmilesState.Radical.Monovalent,
                    [4] = CxSmilesState.Radical.Divalent
                }
            };

            Assert.AreEqual(" |^1:1,5,^2:3|", CxSmilesGenerator.Generate(state, SmiFlavors.CxSmiles, Array.Empty <int>(), new int[] { 7, 6, 5, 4, 3, 2, 1, 0 }));
        }
        public void Coords2d()
        {
            CxSmilesState state = new CxSmilesState
            {
                atomCoords = new List <double[]>
                {
                    new double[] { 0, 1.5, 0 },
                    new double[] { 0, 3, 0 },
                    new double[] { 1.5, 1.5, 0 },
                }
            };

            Assert.AreEqual(" |(1.5,1.5,;,1.5,;,3,)|", CxSmilesGenerator.Generate(state, SmiFlavors.CxCoordinates, Array.Empty <int>(), new int[] { 1, 2, 0 }));
        }
Exemple #5
0
        internal static string Generate(CxSmilesState state, SmiFlavors opts, int[] components, int[] ordering)
        {
            if (!SmiFlavorTool.IsSet(opts, SmiFlavors.CxSmilesWithCoords))
            {
                return("");
            }

            var invorder = Inverse(ordering);

            var sb = new StringBuilder();

            sb.Append(' ');
            sb.Append('|');

            //int invComp(int a, int b) => invorder[a].CompareTo(invorder[b]);
            int comp(int a, int b) => ordering[a].CompareTo(ordering[b]);

            // Fragment Grouping
            if (SmiFlavorTool.IsSet(opts, SmiFlavors.CxFragmentGroup) &&
                state.fragGroups != null &&
                state.fragGroups.Any())
            {
                int maxCompId = 0;
                foreach (int compId_ in components)
                {
                    if (compId_ > maxCompId)
                    {
                        maxCompId = compId_;
                    }
                }

                // get the output component order
                var compMap = new int[maxCompId + 1];
                int compId  = 1;
                foreach (int idx in invorder)
                {
                    var component = components[idx];
                    if (compMap[component] == 0)
                    {
                        compMap[component] = compId++;
                    }
                }
                // index vs number, we need to output index
                for (int i = 0; i < compMap.Length; i++)
                {
                    compMap[i]--;
                }

                int compComp(int a, int b) => compMap[a].CompareTo(compMap[b]);

                var fragGroupCpy = new List <List <int> >(state.fragGroups);
                foreach (var idxs in fragGroupCpy)
                {
                    idxs.Sort(compComp);
                }
                fragGroupCpy.Sort((a, b) => CxSmilesGenerator.Compare(compComp, a, b));

                // C1=CC=CC=C1.C1=CC=CC=C1.[OH-].[Na+]>> |f:0.1,2.3,c:0,2,4,6,8,10|
                sb.Append('f');
                sb.Append(':');
                for (int i = 0; i < fragGroupCpy.Count; i++)
                {
                    if (i > 0)
                    {
                        sb.Append(',');
                    }
                    AppendIntegers(compMap, '.', sb, fragGroupCpy[i]);
                }
            }

            // Atom Labels
            if (SmiFlavorTool.IsSet(opts, SmiFlavors.CxAtomLabel) &&
                state.atomLabels != null && state.atomLabels.Any())
            {
                if (sb.Length > 2)
                {
                    sb.Append(',');
                }
                sb.Append('$');
                int nonempty_cnt = 0;
                foreach (int idx in invorder)
                {
                    if (!state.atomLabels.TryGetValue(idx, out string label))
                    {
                        label = "";
                    }
                    else
                    {
                        nonempty_cnt++;
                    }
                    sb.Append(Encode_alias(label));
                    // don't need to write anymore more ';'
                    if (nonempty_cnt == state.atomLabels.Count)
                    {
                        break;
                    }
                    sb.Append(";");
                }
                sb.Append('$');
            }

            // Atom Values
            if (SmiFlavorTool.IsSet(opts, SmiFlavors.CxAtomValue) &&
                state.atomValues != null && state.atomValues.Any())
            {
                if (sb.Length > 2)
                {
                    sb.Append(',');
                }
                sb.Append("$_AV:");
                int nonempty_cnt = 0;
                foreach (int idx in invorder)
                {
                    var label = state.atomValues[idx];
                    if (string.IsNullOrEmpty(label))
                    {
                        label = "";
                    }
                    else
                    {
                        nonempty_cnt++;
                    }
                    sb.Append(Encode_alias(label));
                    // don't need to write anymore more ';'
                    if (nonempty_cnt == state.atomValues.Count)
                    {
                        break;
                    }
                    sb.Append(";");
                }
                sb.Append('$');
            }

            // 2D/3D Coordinates
            if (SmiFlavorTool.IsSet(opts, SmiFlavors.CxCoordinates) &&
                state.atomCoords != null && state.atomCoords.Any())
            {
                if (sb.Length > 2)
                {
                    sb.Append(',');
                }
                sb.Append('(');
                for (int i = 0; i < ordering.Length; i++)
                {
                    var xyz = state.atomCoords[invorder[i]];
                    if (i != 0)
                    {
                        sb.Append(';');
                    }
                    if (xyz[0] != 0)
                    {
                        sb.Append(Strings.ToSimpleString(xyz[0], 2));
                    }
                    sb.Append(',');
                    if (xyz[1] != 0)
                    {
                        sb.Append(Strings.ToSimpleString(xyz[1], 2));
                    }
                    sb.Append(',');
                    if (xyz[2] != 0)
                    {
                        sb.Append(Strings.ToSimpleString(xyz[2], 2));
                    }
                }
                sb.Append(')');
            }

            // Multi-center/Positional variation bonds
            if (SmiFlavorTool.IsSet(opts, SmiFlavors.CxMulticenter) &&
                state.positionVar != null &&
                state.positionVar.Any())
            {
                if (sb.Length > 2)
                {
                    sb.Append(',');
                }
                sb.Append('m');
                sb.Append(':');

                var multicenters = new List <KeyValuePair <int, IList <int> > >(state.positionVar);

                // consistent output order
                multicenters.Sort((a, b) => comp(a.Key, b.Key));

                for (int i = 0; i < multicenters.Count; i++)
                {
                    if (i != 0)
                    {
                        sb.Append(',');
                    }
                    var e = multicenters[i];
                    sb.Append(ordering[e.Key]);
                    sb.Append(':');
                    var vals = new List <int>(e.Value);
                    vals.Sort(comp);
                    AppendIntegers(ordering, '.', sb, vals);
                }
            }

            // *CCO* |$_AP1;;;;_AP2$,Sg:n:1,2,3::ht|
            if (SmiFlavorTool.IsSet(opts, SmiFlavors.CxPolymer) &&
                state.sgroups != null && state.sgroups.Any())
            {
                var sgroups = new List <PolymerSgroup>(state.sgroups);

                foreach (PolymerSgroup psgroup in sgroups)
                {
                    psgroup.atomset.Sort(comp);
                }

                sgroups.Sort((a, b) =>
                {
                    int cmp = 0;
                    cmp     = string.CompareOrdinal(a.type, b.type);
                    if (cmp != 0)
                    {
                        return(cmp);
                    }
                    cmp = CxSmilesGenerator.Compare(comp, a.atomset, b.atomset);
                    return(cmp);
                });

                for (int i = 0; i < sgroups.Count; i++)
                {
                    if (sb.Length > 2)
                    {
                        sb.Append(',');
                    }
                    sb.Append("Sg:");
                    var sgroup = sgroups[i];
                    sb.Append(sgroup.type);
                    sb.Append(':');
                    AppendIntegers(ordering, ',', sb, sgroup.atomset);
                    sb.Append(':');
                    if (sgroup.subscript != null)
                    {
                        sb.Append(sgroup.subscript);
                    }
                    sb.Append(':');
                    if (sgroup.supscript != null)
                    {
                        sb.Append(sgroup.supscript.ToLowerInvariant());
                    }
                }
            }

            // [C]1[CH][CH]CCC1 |^1:1,2,^3:0|
            if (SmiFlavorTool.IsSet(opts, SmiFlavors.CxRadical) &&
                state.atomRads != null &&
                state.atomRads.Any())
            {
                var radinv = new SortedDictionary <CxSmilesState.Radical, List <int> >();
                foreach (var e in state.atomRads)
                {
                    if (!radinv.TryGetValue(e.Value, out List <int> idxs))
                    {
                        radinv[e.Value] = idxs = new List <int>();
                    }
                    idxs.Add(e.Key);
                }
                foreach (var e in radinv)
                {
                    if (sb.Length > 2)
                    {
                        sb.Append(',');
                    }
                    sb.Append('^');
                    sb.Append((int)e.Key + 1);
                    sb.Append(':');
                    e.Value.Sort(comp);
                    AppendIntegers(ordering, ',', sb, e.Value);
                }
            }

            sb.Append('|');
            if (sb.Length <= 3)
            {
                return("");
            }
            else
            {
                return(sb.ToString());
            }
        }
        public void EmptyCXSMILES()
        {
            CxSmilesState state = new CxSmilesState();

            Assert.AreEqual("", CxSmilesGenerator.Generate(state, SmiFlavors.CxSmiles, Array.Empty <int>(), Array.Empty <int>()));
        }
Exemple #7
0
        /// <summary>
        /// Create a SMILES for a reaction of the flavour specified in the constructor and
        /// write the output order to the provided array.
        /// </summary>
        /// <param name="reaction">CDK reaction instance</param>
        /// <param name="ordering">order of output</param>
        /// <returns>reaction SMILES</returns>
        public string Create(IReaction reaction, int[] ordering)
        {
            var reactants = reaction.Reactants;
            var agents    = reaction.Agents;
            var products  = reaction.Products;

            var reactantPart = reaction.Builder.NewAtomContainer();
            var agentPart    = reaction.Builder.NewAtomContainer();
            var productPart  = reaction.Builder.NewAtomContainer();

            var sgroups = new List <Sgroup>();

            foreach (var reactant in reactants)
            {
                reactantPart.Add(reactant);
                SafeAddSgroups(sgroups, reactant);
            }
            foreach (var agent in agents)
            {
                agentPart.Add(agent);
                SafeAddSgroups(sgroups, agent);
            }
            foreach (var product in products)
            {
                productPart.Add(product);
                SafeAddSgroups(sgroups, product);
            }

            var reactantOrder = new int[reactantPart.Atoms.Count];
            var agentOrder    = new int[agentPart.Atoms.Count];
            var productOrder  = new int[productPart.Atoms.Count];

            var expectedSize = reactantOrder.Length + agentOrder.Length + productOrder.Length;

            if (expectedSize != ordering.Length)
            {
                throw new CDKException($"Output ordering array does not have correct amount of space: {ordering.Length} expected: {expectedSize}");
            }

            // we need to make sure we generate without the CXSMILES layers
            string smi = Create(reactantPart, flavour & ~SmiFlavors.CxSmilesWithCoords, reactantOrder) + ">" +
                         Create(agentPart, flavour & ~SmiFlavors.CxSmilesWithCoords, agentOrder) + ">" +
                         Create(productPart, flavour & ~SmiFlavors.CxSmilesWithCoords, productOrder);

            // copy ordering back to unified array and adjust values
            var agentBeg = reactantOrder.Length;
            var agentEnd = reactantOrder.Length + agentOrder.Length;
            var prodEnd  = reactantOrder.Length + agentOrder.Length + productOrder.Length;

            System.Array.Copy(reactantOrder, 0, ordering, 0, agentBeg);
            System.Array.Copy(agentOrder, 0, ordering, agentBeg, agentEnd - agentBeg);
            System.Array.Copy(productOrder, 0, ordering, agentEnd, prodEnd - agentEnd);
            for (int i = agentBeg; i < agentEnd; i++)
            {
                ordering[i] += agentBeg;
            }
            for (int i = agentEnd; i < prodEnd; i++)
            {
                ordering[i] += agentEnd;
            }

            if (SmiFlavorTool.IsSet(flavour, SmiFlavors.CxSmilesWithCoords))
            {
                var unified = reaction.Builder.NewAtomContainer();
                unified.Add(reactantPart);
                unified.Add(agentPart);
                unified.Add(productPart);
                unified.SetCtabSgroups(sgroups);

                // base CXSMILES state information
                var cxstate = GetCxSmilesState(flavour, unified);

                int[] components = null;

                // extra state info on fragment grouping, specific to reactions
                if (SmiFlavorTool.IsSet(flavour, SmiFlavors.CxFragmentGroup))
                {
                    cxstate.fragGroups = new List <List <int> >();

                    // calculate the connected components
                    components = new ConnectedComponents(GraphUtil.ToAdjList(unified)).GetComponents();

                    // AtomContainerSet is ordered so this is safe, it was actually a set we
                    // would need some extra data structures
                    var tmp = new HashSet <int>();
                    int beg = 0, end = 0;
                    foreach (var mol in reactants)
                    {
                        end = end + mol.Atoms.Count;
                        tmp.Clear();
                        for (int i = beg; i < end; i++)
                        {
                            tmp.Add(components[i]);
                        }
                        if (tmp.Count > 1)
                        {
                            cxstate.fragGroups.Add(new List <int>(tmp));
                        }
                        beg = end;
                    }
                    foreach (var mol in agents)
                    {
                        end = end + mol.Atoms.Count;
                        tmp.Clear();
                        for (int i = beg; i < end; i++)
                        {
                            tmp.Add(components[i]);
                        }
                        if (tmp.Count > 1)
                        {
                            cxstate.fragGroups.Add(new List <int>(tmp));
                        }
                        beg = end;
                    }
                    foreach (var mol in products)
                    {
                        end = end + mol.Atoms.Count;
                        tmp.Clear();
                        for (int i = beg; i < end; i++)
                        {
                            tmp.Add(components[i]);
                        }
                        if (tmp.Count > 1)
                        {
                            cxstate.fragGroups.Add(new List <int>(tmp));
                        }
                        beg = end;
                    }
                }

                smi += CxSmilesGenerator.Generate(cxstate, flavour, components, ordering);
            }

            return(smi);
        }
Exemple #8
0
        /// <summary>
        /// Creates a SMILES string of the flavour specified as a parameter
        /// and write the output order to the provided array.
        /// </summary>
        /// <remarks>
        /// The output order allows one to arrange auxiliary atom data in the
        /// order that a SMILES string will be read. A simple example is seen below
        /// where 2D coordinates are stored with a SMILES string. This method
        /// forms the basis of CXSMILES.
        /// </remarks>
        /// <example>
        /// <include file='IncludeExamples.xml' path='Comments/Codes[@id="NCDK.Smiles.SmilesGenerator_Example.cs+Create_IAtomContainer_int_int"]/*' />
        /// </example>
        /// <param name="molecule">the molecule to write</param>
        /// <param name="order">array to store the output order of atoms</param>
        /// <returns>the SMILES string</returns>
        /// <exception cref="CDKException">a valid SMILES could not be created</exception>
        public static string Create(IAtomContainer molecule, SmiFlavors flavour, int[] order)
        {
            try
            {
                if (order.Length != molecule.Atoms.Count)
                {
                    throw new ArgumentException("the array for storing output order should be the same length as the number of atoms");
                }

                var g = CDKToBeam.ToBeamGraph(molecule, flavour);

                // apply the canonical labelling
                if (SmiFlavorTool.IsSet(flavour, SmiFlavors.Canonical))
                {
                    // determine the output order
                    var labels = Labels(flavour, molecule);

                    g = g.Permute(labels);

                    if ((flavour & SmiFlavors.AtomAtomMapRenumber) == SmiFlavors.AtomAtomMapRenumber)
                    {
                        g = Functions.RenumberAtomMaps(g);
                    }

                    if (!SmiFlavorTool.IsSet(flavour, SmiFlavors.UseAromaticSymbols))
                    {
                        g = g.Resonate();
                    }

                    if (SmiFlavorTool.IsSet(flavour, SmiFlavors.StereoCisTrans))
                    {
                        // FIXME: required to ensure canonical double bond labelling
                        g.Sort(new Graph.VisitHighOrderFirst());

                        // canonical double-bond stereo, output be C/C=C/C or C\C=C\C
                        // and we need to normalise to one
                        g = Functions.NormaliseDirectionalLabels(g);

                        // visit double bonds first, prefer C1=CC=C1 to C=1C=CC1
                        // visit hydrogens first
                        g.Sort(new Graph.VisitHighOrderFirst()).Sort(new Graph.VisitHydrogenFirst());
                    }

                    var smiles = g.ToSmiles(order);

                    // the SMILES has been generated on a reordered molecule, transform
                    // the ordering
                    var canorder = new int[order.Length];
                    for (int i = 0; i < labels.Length; i++)
                    {
                        canorder[i] = order[labels[i]];
                    }
                    System.Array.Copy(canorder, 0, order, 0, order.Length);

                    if (SmiFlavorTool.IsSet(flavour, SmiFlavors.CxSmilesWithCoords))
                    {
                        smiles += CxSmilesGenerator.Generate(GetCxSmilesState(flavour, molecule), flavour, null, order);
                    }

                    return(smiles);
                }
                else
                {
                    string smiles = g.ToSmiles(order);

                    if (SmiFlavorTool.IsSet(flavour, SmiFlavors.CxSmilesWithCoords))
                    {
                        smiles += CxSmilesGenerator.Generate(GetCxSmilesState(flavour, molecule), flavour, null, order);
                    }

                    return(smiles);
                }
            }
            catch (IOException e)
            {
                throw new CDKException(e.Message);
            }
        }