Практикум 2. Хемоинформатика¶
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
Ибупрофен и его алкин-производное¶
Визуализируем молекулу ибупрофена:
ibu = Chem.MolFromSmiles('CC(C)CC1=CC=C(C=C1)C(C)C(=O)O')
display(ibu)
Проверим, что он удовлетворяет правилу пяти Липински:
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
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
lipinski_rule(ibu)
True
Целью этого практикума является поиск кандидатов в аналоги ибупрофена, которые можно было бы синтезировать с помощью реакции клик-химии — азид-алкинового циклоприсоединения, допустим, катализируемого Cu(I). Предлагается искать азидсодержащие соединения в базе PubChem, а алкиновую группу присоединять к ибупрофену; возникает вопрос — как именно?
Возможная структура алкинового производного ибупрофена приведена ниже:
Chem.MolFromSmiles('CC(C)CC1=CC=C(C=C1)C(C)C(=O)OCC#C')
Выбор был сделан исходя из следующих соображений:
- Молекула ибупрофена расположена в кармане связывания таргетного белка (циклооксигеназы) таким образом, что карбоксильная группа контактирует с раствором, а остальные части молекулы погружены в карман. Значит, ввести дополнительный радикал так, чтобы он не мешал ибупрофену помещаться в сайте, можно, присоединив его к карбоксильной группе (см. изображение ниже).
Image('ibu.png', width=500)
- Предложенное соединение можно относительно легко синтезировать из доступных реагентов: карбоксильную группу ибупрофена активировать, превратив в хлорангидрид (например, оксалилхлоридом), затем реакцией нуклеофильного замещения с пропаргиловым спиртом ввести алкиновую группу (схема ниже).
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)
- Оно даже коммерчески доступно (в PubChem приведены ссылки на поставщиков реагентов)
Далее изображен фрагмент молекулы — продукта CuAAC-реакции:
ibu_mod = Chem.MolFromSmiles('CC(C)CC1=CC=C(C=C1)C(C)C(=O)OCC1=CNN=N1', sanitize=False)
ibu_mod
Поиск азидсодержащих реагентов для CuAAC¶
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
len(compounds)
272451
Однако критерии поиска, по которым был получен этот список соеднений, слишком нестрогие. Так, в нем присутствуют свободные азид-ионы, не связанные ни с чем:
Chem.MolFromSmiles(compounds[634]['CanonicalSMILES'])
И, как можно заметить, ничто не запрещает одному SMILES содержать несколько отдельных молекул, поэтому встречается и такое:
Chem.MolFromSmiles(compounds[14818]['CanonicalSMILES'])
Этот беспорядок хорошо бы как-то отфильтровать перед тем, как конструировать модификации ибупрофена
azide_pattern = Chem.MolFromSmarts('[$(N(-*)-N#N),$(N(-*)=N=N)]~[$(N(-N-*)#N),$(N(=N-*)=N)]~[$(N#N-N-*),$(N=N=N-*)]')
(это выражение описывает три связанных друг с другом атома, каждый из которых находится в одном из возможных для азида окружений)
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)
len(mol_compounds_filtered)
289143
Получение формул продуктов¶
Соединения будут конструироваться с помощью замены подструктуры в Mol-объекте. Это должно быть стабильнее, чем заменять подстроку в SMILES, т. к. даже среди канонических SMILES азидная группа может быть обозначена по-разному. Например, в некоторых соединениях она протонирована (на 3-м атоме азота заряд нейтральный вместо -1); тем не менее, Mol-паттерн сможет ее распознать.
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)] # чтобы можно было отобрать уникальные
len(modified_ibu)
43492
Ниже изображены структурные формулы 25 случайных соединений:
np.random.seed(9999)
Draw.MolsToGridImage(np.random.choice(modified_ibu, 25), molsPerRow=5, subImgSize=(250, 250))
Similarity map¶
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
0.1816997720612178
Фрагмент ибупрофена корректно обнаруживается в итоговом веществе
3D-визуализация¶
mol_3d = Chem.AddHs(new_mol)
Chem.AllChem.EmbedMolecule(mol_3d)
AllChem.MMFFOptimizeMolecule(mol_3d, maxIters=500, nonBondedThresh=200)
display(mol_3d)
view = nv.show_rdkit(mol_3d)
view # до конвертации в html здесь был виджет
NGLWidget()
view.download_image('ngl.png')
Image('ngl.png')