1 /* Copyright (C) 2004-2007  Egon Willighagen <egonw@users.sf.net>
2  *
3  * Contact: cdk-devel@lists.sourceforge.net
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public License
7  * as published by the Free Software Foundation; either version 2.1
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 package org.openscience.cdk.smiles.smarts.parser;
20 
21 import java.io.InputStream;
22 import java.io.InputStreamReader;
23 
24 import com.google.common.io.CharStreams;
25 import org.junit.Assert;
26 import org.junit.Test;
27 import org.junit.experimental.categories.Category;
28 import org.openscience.cdk.CDKTestCase;
29 import org.openscience.cdk.DefaultChemObjectBuilder;
30 import org.openscience.cdk.SlowTest;
31 import org.openscience.cdk.aromaticity.Aromaticity;
32 import org.openscience.cdk.aromaticity.ElectronDonation;
33 import org.openscience.cdk.graph.Cycles;
34 import org.openscience.cdk.interfaces.IAtom;
35 import org.openscience.cdk.interfaces.IAtomContainer;
36 import org.openscience.cdk.io.iterator.IteratingSMILESReader;
37 import org.openscience.cdk.smiles.SmilesParser;
38 import org.openscience.cdk.smiles.smarts.SMARTSQueryTool;
39 import org.openscience.cdk.tools.manipulator.AtomContainerManipulator;
40 
41 import static org.hamcrest.CoreMatchers.is;
42 import static org.hamcrest.MatcherAssert.assertThat;
43 import static org.openscience.cdk.smiles.smarts.parser.SMARTSSearchTest.smarts;
44 import static org.openscience.cdk.smiles.smarts.parser.SMARTSSearchTest.smiles;
45 
46 /**
47  * Test recursive smarts
48  *
49  * @author Dazhi Jiao
50  * @cdk.module test-smarts
51  * @cdk.require ant1.6
52  */
53 public class RecursiveTest extends CDKTestCase {
54 
55     private int nmatch;
56     private int nqmatch;
57 
match(String smarts, String smiles)58     public void match(String smarts, String smiles) throws Exception {
59         SMARTSQueryTool sqt = new SMARTSQueryTool(smarts, DefaultChemObjectBuilder.getInstance());
60         SmilesParser sp = new SmilesParser(DefaultChemObjectBuilder.getInstance());
61         IAtomContainer atomContainer = sp.parseSmiles(smiles);
62         AtomContainerManipulator.percieveAtomTypesAndConfigureAtoms(atomContainer);
63         Aromaticity.cdkLegacy().apply(atomContainer);
64         boolean status = sqt.matches(atomContainer);
65         if (status) {
66             nmatch = sqt.countMatches();
67             nqmatch = sqt.getUniqueMatchingAtoms().size();
68         } else {
69             nmatch = 0;
70             nqmatch = 0;
71         }
72     }
73 
74     @Test
testRecursiveSmarts1()75     public void testRecursiveSmarts1() throws Exception {
76         match("[$(*O);$(*CC)]", "O[Po]CC");
77         Assert.assertEquals(1, nmatch);
78         Assert.assertEquals(1, nqmatch);
79     }
80 
81     @Test
testRecursiveSmarts2()82     public void testRecursiveSmarts2() throws Exception {
83         match("[$(*O);$(*CC)]", "OCCC");
84         Assert.assertEquals(1, nmatch);
85         Assert.assertEquals(1, nqmatch);
86     }
87 
88     @Test
testRecursiveSmarts3()89     public void testRecursiveSmarts3() throws Exception {
90         match("[$(*O);$(*CC)]", "CN1C(=O)N(C)C(=O)C(N(C)C=N2)=C12");
91         Assert.assertEquals(0, nmatch);
92         Assert.assertEquals(0, nqmatch);
93     }
94 
95     @Test
testRecursiveSmarts4()96     public void testRecursiveSmarts4() throws Exception {
97         match("[$(*O);$(*CC)]", "c1ncccc1C1CCCN1C");
98         Assert.assertEquals(0, nmatch);
99         Assert.assertEquals(0, nqmatch);
100     }
101 
102     @Test
testRecursiveSmarts5()103     public void testRecursiveSmarts5() throws Exception {
104         match("[$(*O);$(*CC)]", "N12CCC36C1CC(C(C2)=CCOC4CC5=O)C4C3N5c7ccccc76");
105         Assert.assertEquals(1, nmatch);
106         Assert.assertEquals(1, nqmatch);
107     }
108 
109     @Test
testRecursiveSmarts6()110     public void testRecursiveSmarts6() throws Exception {
111         match("[$([CX3]=[CX1]),$([CX3+]-[CX1-])]", "CN1C(=O)N(C)C(=O)C(N(C)C=N2)=C12");
112         Assert.assertEquals(0, nmatch);
113         Assert.assertEquals(0, nqmatch);
114     }
115 
116     @Test
testRecursiveSmarts7()117     public void testRecursiveSmarts7() throws Exception {
118         match("[$([CX3]=[OX1]),$([CX3+]-[OX1-])]", "c1ncccc1C1CCCN1C");
119         Assert.assertEquals(0, nmatch);
120         Assert.assertEquals(0, nqmatch);
121     }
122 
123     @Test
testRecursiveSmarts8()124     public void testRecursiveSmarts8() throws Exception {
125         match("[$([CX3]=[OX1]),$([CX3+]-[OX1-])]", "c1ccccc1C(=O)OC2CC(N3C)CCC3C2C(=O)OC");
126         Assert.assertEquals(2, nmatch);
127         Assert.assertEquals(2, nqmatch);
128     }
129 
130     @Test
testRecursiveSmarts9()131     public void testRecursiveSmarts9() throws Exception {
132         match("[$([CX3]=[OX1]),$([CX3+]-[OX1-])]", "CCN(CC)C(=O)C1CN(C)C2CC3=CNc(ccc4)c3c4C2=C1");
133         Assert.assertEquals(1, nmatch);
134         Assert.assertEquals(1, nqmatch);
135     }
136 
137     @Test
testRecursiveSmarts10()138     public void testRecursiveSmarts10() throws Exception {
139         match("[$([CX3]=[OX1]),$([CX3+]-[OX1-])]", "CC[C+]([O-])C");
140         Assert.assertEquals(1, nmatch);
141         Assert.assertEquals(1, nqmatch);
142     }
143 
144     @Test
testRecursiveSmarts11()145     public void testRecursiveSmarts11() throws Exception {
146         match("[$([CX3]=[OX1]),$([CX3+]-[OX1-])]", "CCCCC[C+]([O-])CCCC");
147         Assert.assertEquals(1, nmatch);
148         Assert.assertEquals(1, nqmatch);
149     }
150 
151     @Test
testRecursiveSmarts12()152     public void testRecursiveSmarts12() throws Exception {
153         match("[$([CX3]=[OX1]),$([CX3+]-[OX1-])]", "CCCCCC(=O)CCCC");
154         Assert.assertEquals(1, nmatch);
155         Assert.assertEquals(1, nqmatch);
156     }
157 
158     @Test
testRecursiveSmarts13()159     public void testRecursiveSmarts13() throws Exception {
160         match("[$([C]aaO);$([C]aaaN)]", "c1c(C)c(O)c(N)cc1");
161         Assert.assertEquals(1, nmatch);
162         Assert.assertEquals(1, nqmatch);
163     }
164 
165     @Test
testRecursiveSmarts14()166     public void testRecursiveSmarts14() throws Exception {
167         match("[$([C]aaO);$([C]aaaN)]", "Oc1c(C)cc(N)cc1");
168         Assert.assertEquals(1, nmatch);
169         Assert.assertEquals(1, nqmatch);
170     }
171 
172     @Test
testRecursiveSmarts15()173     public void testRecursiveSmarts15() throws Exception {
174         match("[$([C]aaO);$([C]aaaN)]", "Oc1c(C)ccc(N)c1");
175         Assert.assertEquals(0, nmatch);
176         Assert.assertEquals(0, nqmatch);
177     }
178 
179     @Test
testRecursiveSmarts16()180     public void testRecursiveSmarts16() throws Exception {
181         match("[$([C]aaO);$([C]aaaN)]", "c1c(C)c(N)c(O)cc1");
182         Assert.assertEquals(0, nmatch);
183         Assert.assertEquals(0, nqmatch);
184     }
185 
186     @Test
testRecursiveSmarts17()187     public void testRecursiveSmarts17() throws Exception {
188         match("[$(C(=O)O),$(P(=O)),$(S(=O)O)]", "CC(=O)O");
189         Assert.assertEquals(1, nmatch);
190         Assert.assertEquals(1, nqmatch);
191 
192         match("[C&$(C(=O)O),P&$(P(=O)),S&$(S(=O)O)]", "CC(=O)O");
193         Assert.assertEquals(1, nmatch);
194         Assert.assertEquals(1, nqmatch);
195     }
196 
197     @Test
testRecursiveSmarts18()198     public void testRecursiveSmarts18() throws Exception {
199         match("[!$([#6,H0,-,-2,-3])]", "CCNC");
200         Assert.assertEquals(1, nmatch);
201         Assert.assertEquals(1, nqmatch);
202 
203         match("[!$([#6,H0,-,-2,-3])]", "CCN(C)C");
204         Assert.assertEquals(0, nmatch);
205         Assert.assertEquals(0, nqmatch);
206     }
207 
208     @Test
testRecursiveSmarts19()209     public void testRecursiveSmarts19() throws Exception {
210         match("[!H0;#7,#8,#9]", "CCN(C)C");
211         Assert.assertEquals(0, nmatch);
212         Assert.assertEquals(0, nqmatch);
213 
214         match("[!H0;#7,#8,#9]", "CC(=O)O");
215         Assert.assertEquals(1, nmatch);
216         Assert.assertEquals(1, nqmatch);
217     }
218 
219     @Test
testRecursiveSmarts20()220     public void testRecursiveSmarts20() throws Exception {
221         match("[C;D2;$(C(=C)(=C))]", "CCC=C=CC");
222         Assert.assertEquals(1, nmatch);
223         Assert.assertEquals(1, nqmatch);
224     }
225 
226     @Test
testRecursiveSmarts21()227     public void testRecursiveSmarts21() throws Exception {
228         match("[C;D2;H2;$(C(C)(C))]", "CC(C)CC");
229         Assert.assertEquals(1, nmatch);
230         Assert.assertEquals(1, nqmatch);
231 
232         match("[C;D2;H2;$(C(C)(C))]", "CC(C)CCC");
233         Assert.assertEquals(2, nmatch);
234         Assert.assertEquals(2, nqmatch);
235     }
236 
237     @Test
testRecursiveSmarts22()238     public void testRecursiveSmarts22() throws Exception {
239         match("[C;D3;H1;$(C(C)(C)(C))]", "C(C)(C)CC(C)(C)C");
240         Assert.assertEquals(1, nmatch);
241         Assert.assertEquals(1, nqmatch);
242 
243         match("[C;D3;H1;$(C(C)(C)(C))]", "C(C)(C)C(C)(C)CC(C)C");
244         Assert.assertEquals(2, nmatch);
245         Assert.assertEquals(2, nqmatch);
246 
247         match("[C;D3;H1;$(C(C)(C)(C))]", "C(C)CC(C)(C)C");
248         Assert.assertEquals(0, nmatch);
249         Assert.assertEquals(0, nqmatch);
250     }
251 
252     @Test
testRecursiveSmarts23()253     public void testRecursiveSmarts23() throws Exception {
254         match("[C;D2;H2;$(C(C)(C))]", "C(C)CC(C)(C)C");
255         Assert.assertEquals(2, nmatch);
256         Assert.assertEquals(2, nqmatch);
257 
258         match("[C;D2;H2;$(C(C)(C))]", "C(C)(C)C(C)C(C)(C)C");
259         Assert.assertEquals(0, nmatch);
260         Assert.assertEquals(0, nqmatch);
261 
262         match("[C;D2;H2;$(C(C)(C))]", "C(C)(C)C(C)C(C)CCCC");
263         Assert.assertEquals(3, nmatch);
264         Assert.assertEquals(3, nqmatch);
265 
266     }
267 
268     @Test
testRecursiveSmarts24()269     public void testRecursiveSmarts24() throws Exception {
270         match("[S;D2;$(S(C)(C))]", "CCSCC");
271         Assert.assertEquals(1, nmatch);
272         Assert.assertEquals(1, nqmatch);
273 
274         match("[S;D2;$(S(C)(C))]", "CCS(=O)(=O)CC");
275         Assert.assertEquals(0, nmatch);
276         Assert.assertEquals(0, nqmatch);
277 
278         match("[S;D2;$(S(C)(C))]", "CCCCC");
279         Assert.assertEquals(0, nmatch);
280         Assert.assertEquals(0, nqmatch);
281 
282     }
283 
284     @Test
testRecursiveSmarts25()285     public void testRecursiveSmarts25() throws Exception {
286         match("[NX3;H2,H1;!$(NC=O)]", "Cc1nc2=NC3=C(C(n2[nH]1)c1cc(cc(c1)F)F)C(=O)CC(C3)c1ccco1");
287         Assert.assertEquals(1, nmatch);
288         Assert.assertEquals(1, nqmatch);
289     }
290 
291     @Test
testRecursiveSmarts34()292     public void testRecursiveSmarts34() throws Exception {
293         match("[NX3;h2,h1,H1,H2;!$(NC=O)]", "NC1CCCC1C(CCNC)Cc1ccccc1N");
294         Assert.assertEquals(3, nmatch);
295         Assert.assertEquals(3, nqmatch);
296     }
297 
298     @Test
testRecursiveSmarts30()299     public void testRecursiveSmarts30() throws Exception {
300         match("[NX3;H2,H1;!$(NC=O)]", "CC1CCCC(C1)N1CCN(CC1)C1CCN(CC1)Cc1ccccc1");
301         Assert.assertEquals(0, nmatch);
302         Assert.assertEquals(0, nqmatch);
303     }
304 
305     @Test
testRecursiveSmarts31()306     public void testRecursiveSmarts31() throws Exception {
307         match("[NX3;H2,H1;!$(NC=O)]", "CCOc1cc2c(cc1/C=C/C(=O)c1ccc(cc1)S(=O)(=O)N1CCCC1)OC(C2)C");
308         Assert.assertEquals(0, nmatch);
309         Assert.assertEquals(0, nqmatch);
310     }
311 
312     @Test
testRecursiveSmarts32()313     public void testRecursiveSmarts32() throws Exception {
314         match("[NX3;H2,H1;!$(NC=O)]", "CN1CCc2cc3c(c(c2C1CC(=O)/C=C/c1ccco1)OC)OCO3");
315         Assert.assertEquals(0, nmatch);
316         Assert.assertEquals(0, nqmatch);
317     }
318 
319     @Test
testRecursiveSmarts33()320     public void testRecursiveSmarts33() throws Exception {
321         match("[NX3;H2,H1;!$(NC=O)]", "Cc1nc2=NC3=C(C(n2[nH]1)c1cc(cc(c1)F)F)C(=O)CC(C3)c1ccco1");
322         Assert.assertEquals(1, nmatch);
323         Assert.assertEquals(1, nqmatch);
324     }
325 
326     @Test
testRecursiveSmarts26()327     public void testRecursiveSmarts26() throws Exception {
328         SMARTSQueryTool sqt = smarts("[NX3;H2,H1;!$(NC=O)]");
329         IAtomContainer smi = smiles("CCCc1cc(=O)nc([nH]1)S");
330         int[] result = SMARTSSearchTest.match(sqt, smi);
331         Assert.assertEquals(0, result[0]);
332         Assert.assertEquals(0, result[1]);
333     }
334 
335     @Test
testRecursiveSmarts26_cdkAromaticModel()336     public void testRecursiveSmarts26_cdkAromaticModel() throws Exception {
337         SMARTSQueryTool sqt = smarts("[NX3;H2,H1;!$(NC=O)]");
338         IAtomContainer smi = smiles("CCCc1cc(=O)nc([nH]1)S");
339         sqt.setAromaticity(new Aromaticity(ElectronDonation.cdk(), Cycles.cdkAromaticSet()));
340         AtomContainerManipulator.percieveAtomTypesAndConfigureAtoms(smi);
341         int[] result = SMARTSSearchTest.match(sqt, smi);
342         Assert.assertEquals(1, result[0]);
343         Assert.assertEquals(1, result[1]);
344     }
345 
346     @Test
testRecursiveSmarts27_cdkAromaticModel()347     public void testRecursiveSmarts27_cdkAromaticModel() throws Exception {
348         SMARTSQueryTool sqt = smarts("[NX3;H2,H1;!$(NC=O)]");
349         IAtomContainer smi = smiles("CCCc1nc(c2n1[nH]c(nc2=O)c1cc(ccc1OCC)S(=O)(=O)N1CCN(CC1)CC)C");
350         sqt.setAromaticity(new Aromaticity(ElectronDonation.cdk(), Cycles.cdkAromaticSet()));
351         AtomContainerManipulator.percieveAtomTypesAndConfigureAtoms(smi);
352         int[] result = SMARTSSearchTest.match(sqt, smi);
353         Assert.assertEquals(1, result[0]);
354         Assert.assertEquals(1, result[1]);
355     }
356 
357     @Test
testRecursiveSmarts27()358     public void testRecursiveSmarts27() throws Exception {
359         SMARTSQueryTool sqt = smarts("[NX3;H2,H1;!$(NC=O)]");
360         IAtomContainer smi = smiles("CCCc1nc(c2n1[nH]c(nc2=O)c1cc(ccc1OCC)S(=O)(=O)N1CCN(CC1)CC)C");
361         int[] result = SMARTSSearchTest.match(sqt, smi);
362         Assert.assertEquals(0, result[0]);
363         Assert.assertEquals(0, result[1]);
364     }
365 
366     @Test
testRecursive28()367     public void testRecursive28() throws Exception {
368         SMARTSQueryTool sqt = smarts("[NX3;H2,H1;!$(NC=O)]");
369         IAtomContainer smi = smiles("Cc1ccc[n+]2c1[nH]cc(c2=O)c1n[nH]nn1");
370         int[] result = SMARTSSearchTest.match(sqt, smi);
371         Assert.assertEquals(0, result[0]);
372         Assert.assertEquals(0, result[1]);
373     }
374 
375     @Test
testRecursive28_cdkAromaticModel()376     public void testRecursive28_cdkAromaticModel() throws Exception {
377         SMARTSQueryTool sqt = smarts("[NX3;H2,H1;!$(NC=O)]");
378         IAtomContainer smi = smiles("Cc1ccc[n+]2c1[nH]cc(c2=O)c1n[nH]nn1");
379         sqt.setAromaticity(new Aromaticity(ElectronDonation.cdk(), Cycles.cdkAromaticSet()));
380         AtomContainerManipulator.percieveAtomTypesAndConfigureAtoms(smi);
381         int[] result = SMARTSSearchTest.match(sqt, smi);
382         Assert.assertEquals(1, result[0]);
383         Assert.assertEquals(1, result[1]);
384     }
385 
386     @Test
testRecursive29()387     public void testRecursive29() throws Exception {
388         SMARTSQueryTool sqt = smarts("[NX3;H2,H1;!$(NC=O)]");
389         IAtomContainer smi = smiles("Cc1cc(=O)c(c[nH]1)C(=O)NC(c1ccc(cc1)O)C(=O)NC1C(=O)N2C1SCC(=C2C(=O)O)CSc1nnnn1C");
390         int[] result = SMARTSSearchTest.match(sqt, smi);
391         Assert.assertEquals(0, result[0]);
392         Assert.assertEquals(0, result[1]);
393     }
394 
395     @Test
nestedRecursion()396     public void nestedRecursion() throws Exception {
397         assertThat(SMARTSSearchTest.match("[$(*C[$(*C)$(**N)])]", "CCCCN"), is(new int[]{2, 2}));
398         assertThat(SMARTSSearchTest.match("[$(*C[$(*C)$(**N)])]", "CCN"), is(new int[]{1, 1}));
399     }
400 
401     @Test
testRecursive29_cdkAromaticModel()402     public void testRecursive29_cdkAromaticModel() throws Exception {
403         SMARTSQueryTool sqt = smarts("[NX3;H2,H1;!$(NC=O)]");
404         IAtomContainer smi = smiles("Cc1cc(=O)c(c[nH]1)C(=O)NC(c1ccc(cc1)O)C(=O)NC1C(=O)N2C1SCC(=C2C(=O)O)CSc1nnnn1C");
405         sqt.setAromaticity(new Aromaticity(ElectronDonation.cdk(), Cycles.cdkAromaticSet()));
406         AtomContainerManipulator.percieveAtomTypesAndConfigureAtoms(smi);
407         int[] result = SMARTSSearchTest.match(sqt, smi);
408         Assert.assertEquals(1, result[0]);
409         Assert.assertEquals(1, result[1]);
410     }
411 
412     @Category(SlowTest.class)
413     @Test
testBasicAmineOnDrugs_cdkAromaticModel()414     public void testBasicAmineOnDrugs_cdkAromaticModel() throws Exception {
415         String filename = "drugs.smi";
416         InputStream ins = this.getClass().getResourceAsStream(filename);
417         IteratingSMILESReader reader = new IteratingSMILESReader(ins, DefaultChemObjectBuilder.getInstance());
418 
419         SMARTSQueryTool sqt = new SMARTSQueryTool("[NX3;H2,H1;!$(NC=O)]", DefaultChemObjectBuilder.getInstance());
420         sqt.setAromaticity(new Aromaticity(ElectronDonation.cdk(), Cycles.cdkAromaticSet()));
421         int nmatch = 0;
422         int nmol = 0;
423         READ: while (reader.hasNext()) {
424             IAtomContainer container = reader.next();
425             AtomContainerManipulator.percieveAtomTypesAndConfigureAtoms(container);
426 
427             // skip un-typed atoms, they can't be run through the CDK aromatic
428             // model
429             for (IAtom atom : container.atoms()) {
430                 if (atom.getAtomTypeName() == null) {
431                     continue READ;
432                 }
433             }
434 
435             if (sqt.matches(container)) {
436                 nmatch++;
437             }
438             nmol++;
439         }
440         reader.close();
441         Assert.assertEquals(141, nmol);
442         Assert.assertEquals(4, nmatch);
443     }
444 
445     @Category(SlowTest.class)
446     @Test
testBasicAmineOnDrugs()447     public void testBasicAmineOnDrugs() throws Exception {
448         String filename = "drugs.smi";
449         InputStream ins = this.getClass().getResourceAsStream(filename);
450 
451         SMARTSQueryTool sqt = new SMARTSQueryTool("[NX3;H2,H1;!$(NC=O)]", DefaultChemObjectBuilder.getInstance());
452 
453         // iterating SMILES reader doesn't allow us to turn off automatic aromaticity
454         // perception
455         SmilesParser sp = new SmilesParser(DefaultChemObjectBuilder.getInstance());
456 
457         int nmatch = 0;
458         int nmol = 0;
459         for (String smi : CharStreams.readLines(new InputStreamReader(ins))) {
460             IAtomContainer container = sp.parseSmiles(smi.split("\t")[0]);
461             if (sqt.matches(container)) {
462                 nmatch++;
463             }
464             nmol++;
465         }
466         Assert.assertEquals(141, nmol);
467         Assert.assertEquals(0, nmatch);
468     }
469 
470     /**
471      * @cdk.bug 1312
472      */
473     @Test
recursiveComponentGrouping()474     public void recursiveComponentGrouping() throws Exception {
475         assertThat(SMARTSSearchTest.match("[O;D1;$(([a,A]).([A,a]))][CH]=O", "OC=O.c1ccccc1"), is(new int[]{1, 1}));
476         assertThat(SMARTSSearchTest.match("[O;D1;$(([a,A]).([A,a]))][CH]=O", "OC=O"), is(new int[]{0, 0}));
477     }
478 
479     /**
480      * @cdk.bug 844
481       */
482     @Test
bug844()483     public void bug844() throws Exception {
484         assertThat(SMARTSSearchTest.match("[*R0]-[$([NRD3][CR]=O)]", "N1(CC)C(=O)CCCC1"), is(new int[]{1, 1}));
485     }
486 }
487