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