# Substructure search with RDKit

I'm trying to search for substructures with RDKit.

I would like to search for structure 1 as substructure in other structures. I don't understand why it doesn't find this structure in structures 3 and 5. I assume this is not a bug in RDKit, but more the result of my limited chemistry understanding. Something with aromaticity? Something wrong with the structures? Can someone explain to me why it doesn't find structure 1 in structures 3 and 5? How should the search structure look like that I will find all structures 2-5?

from rdkit import Chem

smiles_list = ['C12=CC=CC=C1C3=CC=CC4=C3C2=CC=C4', 'C12=CC=CC=C1C3=CC=C4C5=C(C=CC2=C35)C6=C4C=CC=C6', 'C12=CC=CC=C1C3=CC=C4C5=C(C=CC2=C35)C=C4', 'C12=CC=CC=C1C3=CC=C4C5=C(C=CC2=C35)C6=C4C=C7CCCCC7=C6', 'C12=CC=CC=C1C3=CC=C4C5=C(C=CC2=C35)C6=C4C=C7CC8=CC=CC=C8CC7=C6']

pattern = Chem.MolFromSmiles(smiles_list[0])
for idx,smiles in enumerate(smiles_list):
m = Chem.MolFromSmiles(smiles)
print("Structure {}: pattern found {}".format(idx+1,m.HasSubstructMatch(pattern)))

# Result:
# Structure 1: pattern found True
# Structure 2: pattern found True
# Structure 3: pattern found False
# Structure 4: pattern found True
# Structure 5: pattern found False

• I'll point out that there's a very active rdkit-discuss mailing list that would be a good place to ask. – Geoff Hutchison Feb 28 '20 at 17:30
• thank you for pointing to this. I think I found a solution (see my answer), but I'm not sure if this solution might cause some other surprises or unexpected results. Should I discuss this on the mailing list? What do you think? – theozh Feb 28 '20 at 17:41
• @GeoffHutchison See above response. – Melanie Shebel Feb 28 '20 at 21:49
• @theozh Just a heads up, if you reply to a comment, you have to tag the person, otherwise, they won't see it. Welcome to Chem.SE! I love your question and will be following it. Very interesting work you're doing. – Melanie Shebel Feb 28 '20 at 21:50
• I just realized you're not new to chem.se, but welcome anyway! You have some seriously cool questions! – Melanie Shebel Feb 28 '20 at 21:51

I'm not sure about why it's not matching but when I carry out substructure matches in rdkit I use SMARTS instead https://www.daylight.com/dayhtml/doc/theory/theory.smarts.html

Here is what I would have done.

from rdkit import Chem

smiles_list = ['C12=CC=CC=C1C3=CC=CC4=C3C2=CC=C4', 'C12=CC=CC=C1C3=CC=C4C5=C(C=CC2=C35)C6=C4C=CC=C6', 'C12=CC=CC=C1C3=CC=C4C5=C(C=CC2=C35)C=C4', 'C12=CC=CC=C1C3=CC=C4C5=C(C=CC2=C35)C6=C4C=C7CCCCC7=C6', 'C12=CC=CC=C1C3=CC=C4C5=C(C=CC2=C35)C6=C4C=C7CC8=CC=CC=C8CC7=C6', 'c1ccccc1']

pattern = Chem.MolFromSmarts('[#6]12~[#6][#6]~[#6][#6]~[#6]1[#6]3~[#6][#6]~[#6][#6]4~[#6]3[#6]2~[#6][#6]~[#6]4')
for idx,smiles in enumerate(smiles_list):
m = Chem.MolFromSmiles(smiles)
print("Structure {}: pattern found {}".format(idx+1,m.HasSubstructMatch(pattern)))


and the output

Structure 1: pattern found True
Structure 2: pattern found True
Structure 3: pattern found True
Structure 4: pattern found True
Structure 5: pattern found True
Structure 6: pattern found False


I've added a Benzene smiles to show that it's not just matching everything. The SMARTS pattern I made probably could be better I think. I hope this helps anyway.

• Thank you for testing. Interesting. Then I need to check how I can reliably convert SMILES to SMARTS. Why it doesn't work with SMILES? Still a bug in RDKit? – theozh Feb 28 '20 at 9:39
• I'm not sure why it doesn't work with SMILES, I've always used the m.HasSubstructMatch method with SMARTS. But learning SMARTS is pretty easy, its similar to SMILES just follow the link in the answer. – Unskilled Feb 28 '20 at 9:45
• I just read that a SMILES is also a valid SMARTS expression. But if I use Chem.MolFromSmarts(smiles), all matches are False. – theozh Feb 28 '20 at 9:55
• Yes most SMILES patterns are valid SMARTS but the SMARTS pattern 'C12=CC=CC=C1C3=CC=CC4=C3C2=CC=C4' is not the same as '[#6]12~[#6][#6]~[#6][#6]~[#6]1[#6]3~[#6][#6]~[#6][#6]4~[#6]3[#6]2~[#6][#6]~[#6]4'. The [#6] means any carbon atom aromatic or aliphatic while in the first pattern the C means an aliphatic carbon. Since at some point rdkit will make certain carbons in your molecules aromatic it will mean that it will not match. Also ~ means any bond while = in the first pattern is a double bond rdkit will at some point change some of your molecules bonds to aromatic bonds so will not match. – Unskilled Feb 28 '20 at 10:10
• OK. I see. But how do I get a SMARTS from a SMILES? So far, couldn't find it in RDKit. From SMARTS to Mol and from Mol to SMILES, but not the other way round. OBabel? I wanted to avoid an extra tool. I need to search further. – theozh Feb 28 '20 at 10:14

The following looks like a solution, unless anybody disproves it. Thank you @Unskilled, for pointing me in the right direction.

If you use structure 1 with smiles_1a: C12=CC=CC=C1C3=CC=CC4=C3C2=CC=C4, you will not find structures 3 and 5. If you take OpenBabel and convert smiles_1a to SMILES you get: c12ccccc1c1cccc3c1ccc3. If you take this SMILES string and convert it to mol via Chem.MolFromSmarts() you will find all structures. However, I don't want to use additional external tools.

So, in RDKit, if you convert smiles_1a to mol and this mol back to SMILES again, you get c1ccc2c(c1)-c1cccc3cccc-2c13. If you search with this, you will still not find structures 3 and 5. Probably because of the defined single bonds. However, if you replace - by ~, you get smiles_1b: c1ccc2c(c1)~c1cccc3cccc~2c13. With this, you will find also structures 3 and 5. Happy End, hopefully.

Code: (I also added Benzene to have a non-match)

from rdkit import Chem

smiles_list = ['C12=CC=CC=C1C3=CC=CC4=C3C2=CC=C4', 'C12=CC=CC=C1C3=CC=C4C5=C(C=CC2=C35)C6=C4C=CC=C6', 'C12=CC=CC=C1C3=CC=C4C5=C(C=CC2=C35)C=C4', 'C12=CC=CC=C1C3=CC=C4C5=C(C=CC2=C35)C6=C4C=C7CCCCC7=C6', 'C12=CC=CC=C1C3=CC=C4C5=C(C=CC2=C35)C6=C4C=C7CC8=CC=CC=C8CC7=C6','c1ccccc1']

def search_structure(pattern):
for idx,smiles in enumerate(smiles_list):
m = Chem.MolFromSmiles(smiles)
print("Structure {}: pattern found {}".format(idx+1,m.HasSubstructMatch(pattern)))

smiles_1a  = smiles_list[0]
pattern_1a = Chem.MolFromSmiles(smiles_list[0])
smiles_1b  = Chem.MolToSmiles(pattern_1a).replace('-','~')
pattern_1b = Chem.MolFromSmarts(smiles_1b)

print("\nSMILES 1a: {}".format(smiles_1a))
search_structure(pattern_1a)
print("\nSMILES 1b: {}".format(smiles_1b))
search_structure(pattern_1b)


Result:

SMILES 1a: C12=CC=CC=C1C3=CC=CC4=C3C2=CC=C4
Structure 1: pattern found True
Structure 2: pattern found True
Structure 3: pattern found False
Structure 4: pattern found True
Structure 5: pattern found False
Structure 6: pattern found False

SMILES 1b: c1ccc2c(c1)~c1cccc3cccc~2c13
Structure 1: pattern found True
Structure 2: pattern found True
Structure 3: pattern found True
Structure 4: pattern found True
Structure 5: pattern found True
Structure 6: pattern found False