Практикум 2. Хемоинформатика¶

In [1]:
from rdkit import Chem
from rdkit.Chem import AllChem
from rdkit.Chem.Draw import IPythonConsole 
from rdkit.Chem.Draw import SimilarityMaps
from rdkit.Chem import Draw
from IPython.display import display, Image
from rdkit.Chem import Lipinski
import numpy as np
import pubchempy as pcp
import nglview as nv

Ибупрофен и его алкин-производное¶

Визуализируем молекулу ибупрофена:

In [2]:
ibu = Chem.MolFromSmiles('CC(C)CC1=CC=C(C=C1)C(C)C(=O)O')
display(ibu)
No description has been provided for this image

Проверим, что он удовлетворяет правилу пяти Липински:

In [3]:
print(Lipinski.NumHDonors(ibu))
print(Lipinski.NumHAcceptors(ibu))
print(Lipinski.rdMolDescriptors.CalcExactMolWt(ibu))
print(Lipinski.rdMolDescriptors.CalcCrippenDescriptors(ibu)[0])
1
1
206.130679816
3.073200000000001
In [4]:
lipinski_rule = lambda mol: Lipinski.NumHDonors(mol) <= 5 and \
                            Lipinski.NumHAcceptors(mol) <= 10 and \
                            Lipinski.rdMolDescriptors.CalcExactMolWt(mol) <= 500 and \
                            Lipinski.rdMolDescriptors.CalcCrippenDescriptors(mol)[0] <= 5
In [5]:
lipinski_rule(ibu)
Out[5]:
True

Целью этого практикума является поиск кандидатов в аналоги ибупрофена, которые можно было бы синтезировать с помощью реакции клик-химии — азид-алкинового циклоприсоединения, допустим, катализируемого Cu(I). Предлагается искать азидсодержащие соединения в базе PubChem, а алкиновую группу присоединять к ибупрофену; возникает вопрос — как именно?

Возможная структура алкинового производного ибупрофена приведена ниже:

In [6]:
Chem.MolFromSmiles('CC(C)CC1=CC=C(C=C1)C(C)C(=O)OCC#C')
Out[6]:
No description has been provided for this image

Выбор был сделан исходя из следующих соображений:

  1. Молекула ибупрофена расположена в кармане связывания таргетного белка (циклооксигеназы) таким образом, что карбоксильная группа контактирует с раствором, а остальные части молекулы погружены в карман. Значит, ввести дополнительный радикал так, чтобы он не мешал ибупрофену помещаться в сайте, можно, присоединив его к карбоксильной группе (см. изображение ниже).
In [7]:
Image('ibu.png', width=500)
Out[7]:
No description has been provided for this image
  1. Предложенное соединение можно относительно легко синтезировать из доступных реагентов: карбоксильную группу ибупрофена активировать, превратив в хлорангидрид (например, оксалилхлоридом), затем реакцией нуклеофильного замещения с пропаргиловым спиртом ввести алкиновую группу (схема ниже).
In [8]:
react_1 = AllChem.ReactionFromSmarts('CC(C)CC1=CC=C(C=C1)C(C)C(=O)O>ClC(=O)C(=O)Cl>CC(C)CC1=CC=C(C=C1)C(C)C(=O)Cl', useSmiles=True)
react_2 = AllChem.ReactionFromSmarts('CC(C)CC1=CC=C(C=C1)C(C)C(=O)Cl>C#CCO>CC(C)CC1=CC=C(C=C1)C(C)C(=O)OCC#C', useSmiles=True)
display(react_1, react_2)
No description has been provided for this image
No description has been provided for this image
  1. Оно даже коммерчески доступно (в PubChem приведены ссылки на поставщиков реагентов)

Далее изображен фрагмент молекулы — продукта CuAAC-реакции:

In [9]:
ibu_mod = Chem.MolFromSmiles('CC(C)CC1=CC=C(C=C1)C(C)C(=O)OCC1=CNN=N1', sanitize=False)
ibu_mod
Out[9]:
No description has been provided for this image

Поиск азидсодержащих реагентов для CuAAC¶

In [10]:
compounds = []

%store -r compounds

if not compounds:
    per_page = 10 ** 4
    for smiles in ['N=N=N', 'NN#N']:
        for i in range(100):
            try:
                page = pcp.get_properties(properties = 'CanonicalSMILES',
                                          identifier = smiles,
                                          namespace = 'smiles', 
                                          searchtype = 'substructure',
                                          RingsNotEmbedded = True,
                                          listkey_count = per_page,
                                          listkey_start = i * per_page)
            except pcp.ServerError as error:
                print('Search finished:', error)
                break
            print('Retrieved page {} of {} search: {} results'.format(i + 1, smiles, len(page)))
            compounds.extend(page)
    %store compounds
In [11]:
len(compounds)
Out[11]:
272451

Однако критерии поиска, по которым был получен этот список соеднений, слишком нестрогие. Так, в нем присутствуют свободные азид-ионы, не связанные ни с чем:

In [12]:
Chem.MolFromSmiles(compounds[634]['CanonicalSMILES'])
Out[12]:
No description has been provided for this image

И, как можно заметить, ничто не запрещает одному SMILES содержать несколько отдельных молекул, поэтому встречается и такое:

In [13]:
Chem.MolFromSmiles(compounds[14818]['CanonicalSMILES'])
Out[13]:
No description has been provided for this image

Этот беспорядок хорошо бы как-то отфильтровать перед тем, как конструировать модификации ибупрофена

In [14]:
azide_pattern = Chem.MolFromSmarts('[$(N(-*)-N#N),$(N(-*)=N=N)]~[$(N(-N-*)#N),$(N(=N-*)=N)]~[$(N#N-N-*),$(N=N=N-*)]')

(это выражение описывает три связанных друг с другом атома, каждый из которых находится в одном из возможных для азида окружений)

In [15]:
mol_compounds_filtered = []
for entry in compounds:
    mol = Chem.MolFromSmiles(entry['CanonicalSMILES'], sanitize=False) # построим Mol-объект из SMILES
    for frag in Chem.rdmolops.GetMolFrags(mol, asMols=True, sanitizeFrags=False): # разобъем на ковалентно связанные фрагменты
        if frag.HasSubstructMatch(azide_pattern): # проверим наличие азида
            mol_compounds_filtered.append(frag)
In [16]:
len(mol_compounds_filtered)
Out[16]:
289143

Получение формул продуктов¶

Соединения будут конструироваться с помощью замены подструктуры в Mol-объекте. Это должно быть стабильнее, чем заменять подстроку в SMILES, т. к. даже среди канонических SMILES азидная группа может быть обозначена по-разному. Например, в некоторых соединениях она протонирована (на 3-м атоме азота заряд нейтральный вместо -1); тем не менее, Mol-паттерн сможет ее распознать.

In [ ]:
modified_ibu_smiles = []
for mol in mol_compounds_filtered:
    products = AllChem.ReplaceSubstructs(mol, azide_pattern, ibu_mod, replacementConnectionPoint=18)
    for product in products: # получается список всех возможных продуктов, что полезно, если в молекуле больше одной азидной группы
        try:
            Chem.SanitizeMol(product)
        except Chem.AtomValenceException:
            continue
        if lipinski_rule(product):
            modified_ibu_smiles.append(Chem.MolToSmiles(product, kekuleSmiles=True, canonical=True)) # продукты конвертируются в SMILES
modified_ibu = [Chem.MolFromSmiles(smiles) for smiles in np.unique(modified_ibu_smiles)] # чтобы можно было отобрать уникальные
In [18]:
len(modified_ibu)
Out[18]:
43492

Ниже изображены структурные формулы 25 случайных соединений:

In [19]:
np.random.seed(9999)
Draw.MolsToGridImage(np.random.choice(modified_ibu, 25), molsPerRow=5, subImgSize=(250, 250))
Out[19]:
No description has been provided for this image

Similarity map¶

In [20]:
np.random.seed(9999)
new_mol = np.random.choice(modified_ibu)
fig, maxweight = SimilarityMaps.GetSimilarityMapForFingerprint(ibu, new_mol, SimilarityMaps.GetMorganFingerprint)
maxweight
[19:36:41] DEPRECATION WARNING: please use MorganGenerator
[19:36:41] DEPRECATION WARNING: please use MorganGenerator
Out[20]:
0.1816997720612178
No description has been provided for this image

Фрагмент ибупрофена корректно обнаруживается в итоговом веществе

3D-визуализация¶

In [21]:
mol_3d = Chem.AddHs(new_mol)
Chem.AllChem.EmbedMolecule(mol_3d)
AllChem.MMFFOptimizeMolecule(mol_3d, maxIters=500, nonBondedThresh=200)
display(mol_3d)
No description has been provided for this image
In [22]:
view = nv.show_rdkit(mol_3d)
view # до конвертации в html здесь был виджет
NGLWidget()
In [25]:
view.download_image('ngl.png')
In [26]:
Image('ngl.png')
Out[26]:
No description has been provided for this image