Thilo asked a similar question on the rdkit-discuss
mailing list, where Andrew Dalke chimed in with this response, which he gave me permission to post here. The answer uses the python-based rdkit library to give examples of converting between SMILES and SMARTS and other tasks.
From Andrew Dalke:
On Apr 19, 2017, at 12:03, Thilo Bauer wrote:
Is converting SMARTS to SMILES a "lossless" operation, or does one loose
information on doing so?
It is obviously not lossless if you include terms that cannot be represented in SMILES.
>>> from rdkit import Chem
>>> Chem.MolToSmiles(Chem.MolFromSmarts("[C,N]"))
'C'
or which don't make sense as a molecule:
>>> Chem.MolToSmiles(Chem.MolFromSmarts("c"))
'c'
>>> Chem.MolFromSmiles("c")
[23:02:24] non-ring atom 0 marked aromatic
It also loses some information which could be represented in SMILES:
>>> Chem.MolToSmiles(Chem.MolFromSmarts("[NH4+]"))
'N'
>>> Chem.MolToSmiles(Chem.MolFromSmarts("C[N+]1(C)CCCCC1"))
'CN1(C)CCCCC1'
>>> Chem.MolToSmiles(Chem.MolFromSmarts("[12C]"), isomericSmiles=True)
'C'
Do be careful if you want to handle aromatic atoms and bonds:
>>> Chem.MolToSmiles(Chem.MolFromSmarts("[#6]:1:[#6]:[#6]:[#6]:[#6]:[#6]:1"))
'C1:C:C:C:C:C:1'
>>> Chem.MolToSmiles(Chem.MolFromSmarts("c=1-c=c-c=c-c=1"))
'c1=c-c=c-c=c-1'
Background:
I've got three different SMARTS strings representing the same structure
- at least when depicting it. Also all three strings result in the exact
same SMILES (see code and output below).
It looks like you want SMARTS canonicalization.
In general this is hard, because SMARTS can include boolean expressions and recursive SMARTS.
If you limit yourself to patterns like '[#6]-1=[#6]-[#6]...'
, with only atomic numbers and single/double/triple bonds, then I think RDKit will do what you want.
[[CF: Andrew Dalke also had important answer-level commentary on R.M.'s answer, which I copy below.]]
From chemistry stack exchange, an answer contributed by user R.M.:
SMARTS is deliberately designed to be a superset of SMILES. That is, any valid SMILES depiction should also be a valid SMARTS query, one that will retrieve the very structure that the SMILES string depicts.
Except, that last clause isn't true. Try matching tritium against itself.
>>> from rdkit import Chem
>>> mol = Chem.MolFromSmiles("[3H]")
>>> pat = Chem.MolFromSmarts("[3H]")
>>> mol.HasSubstructMatch(pat)
False
For hydrogens you must use '#1', because H in SMARTS means something different.
>>> pat2 = Chem.MolFromSmarts("[3#1]")
>>> mol.HasSubstructMatch(pat2)
True
SMILES input under Daylight and most other toolkits gets normalized to the chemistry model, including aromaticity perception:
>>> mol = Chem.MolFromSmiles("C1=CC=CC=C1")
>>> pat = Chem.MolFromSmarts("C1=CC=CC=C1")
>>> mol.HasSubstructMatch(pat)
False
>>> pat2 = Chem.MolFromSmarts("c1ccccc1")
>>> mol.HasSubstructMatch(pat2)
True
RDKit also does a small amount of additional normalization, or 'sanitization' to use the RDKit term. For example, it will convert "neutral 5 coordinate Ns with double bonds to Os to the zwitterionic form" (see GraphMol/MolOps.cpp):
>>> s = "CN(=O)=O"
>>> mol = Chem.MolFromSmiles(s)
>>> pat = Chem.MolFromSmarts(s)
>>> mol.HasSubstructMatch(pat)
False
>>> Chem.MolToSmiles(mol)
'C[N+](=O)[O-]'
I believe that the output SMILES from a toolkit, assuming that the SMILES doesn't have an explicit hydrogen, can be used a SMARTS which will match the molecule made from that same SMILES, by that same toolkit.