from rdkit import Chem
from rdkit.Chem import AllChem
from rdkit import RDConfig
from rdkit.Chem.Draw import IPythonConsole
from rdkit.Chem import Draw
import rdkit.Chem.Lipinski as Lipinksi
from rdkit.Chem.Draw import SimilarityMaps
import pubchempy as pcp
import numpy as np
from IPython.display import display,Image
Рассмотрим ибупрофен
ibu = Chem.MolFromSmiles('CC(C)CC1=CC=C(C=C1)C(C)C(=O)O')
ibu
Он представляет собой бензольное кольцо с двумя заместителями в пара-положении: один полностью алифатический, другой с карбоксильной группой.
Ибупрофен связывается в активном сайте циклооксигеназы 2, ингибируя тем самым осуществляемое ею превращение арахидоновой кислоты в простогландин H2 (медиатор воспаления).
Взаимодействие выглядит следующим образом (согласно инструменту PoseView по структуре 4PH9):
Image(filename='4ph9_IBP_A_601.png', width=500)
Вся гидрофобная часть молекулы находится в кармане, а карбоксильная группа расположена на выходе из кармана и активно взаимодейвствует с окружающими полярными и заряженными группами.
В задании предлагается модицицировать ибупрофен введением дополнительного фрагмента по клик-реакции Хьюстена (азид-алкин циклоприсоединение)
Image(url='https://upload.wikimedia.org/wikipedia/commons/thumb/f/ff/CuAAC-triazole-synthesis.png/1920px-CuAAC-triazole-synthesis.png', width=500)
Для этого в нём должна быть тройная связь.
Поскольку внутри кармана ряд ли найдётся место для дополнительного крупного фрагмента, введём её со стороны карбоксильной группы, например, через эфирную связь — это получится достаточно гибкое соединение, которое, к тому же, по идее достаточно несложно синтезировать. Из минусов — мы потеряем минус на карбоксигруппе, который взаимодействует с плюсом на аргинине.
Разумность такого подхода подтверждаются различными работами, применяющими его (а также вариации, например, с заменой карбоксильной группы на карбамидую) и добивающимися в результате улучшения фармакологических свойств: Lolli et al, 2001, Vasincu et al, 2021, Abbas et al, 2022.
click_R_before = 'C#C' # алкильная группа
click_R_after = 'C1=CNN=N1' # кольцо после присоединения азида
click_R1, click_R2 = 'N1C=C', 'N=N1'
# acidic_R = 'C(C(=O)O)C'
aliphatic_R = 'CC(C)C'
# benzene_acidic = f'C2=CC({acidic_R})=CC=C2'
benzene_aliphatic = f'C2=CC=C({aliphatic_R})C=C2'
display(Draw.MolsToGridImage(
[Chem.MolFromSmiles(x) for x in [f'C({benzene_aliphatic})(C(=O)O({click_R_before}))C',
f'C({benzene_aliphatic})(C(=O)O({click_R_after}))C',
# перепишем так, чтобы азоты были по краям молекулы — для корректной вставки далее
f'{click_R1}(OC(=O)C(C){benzene_aliphatic}){click_R2}'
]
], useSVG=False, molsPerRow=3, subImgSize=(200, 200)))
ibu_template = f'{click_R1}(OC(=O)C(C){benzene_aliphatic}){click_R2}'
Соединение выше будет использовано как шаблон, в котором перебираемые радикалы будут присоединяться к нижнему азоту.
Получим из PubChem все молекулы, содержащие азидный фрагмент
compounds = []
per_page = 10**5
for smiles in ["N=N=N", "NN#N",]:
for i in range(200):
try:
a = pcp.get_properties(
properties="CanonicalSMILES", # свойства, которые получаем для находок
identifier=smiles, # query
namespace="smiles", # что представляет собой query
searchtype="substructure", # искать как подструктуру
RingsNotEmbedded=True, # не учитывать query-фрагменты, когда они часть кольца
listkey_count=per_page, # сколько молекул получить
listkey_start=i*per_page # начиная с какой получать
)
compounds.extend(a)
except pcp.ServerError:
print('all here')
break
print("Retrieved page {} of {} search".format(i+1, smiles))
Retrieved page 1 of N=N=N search Retrieved page 2 of N=N=N search Retrieved page 3 of N=N=N search all here Retrieved page 1 of NN#N search all here
len(compounds)
242269
Вставим в находки место азидной группы наш видоизменённый ибупрофен, отфильтруем получившиеся молекулы по соответсвию правилам пяти Липински
def pass_Lipinksi(mol):
return \
Lipinksi.NumHDonors(mol) <= 5 and \
Lipinksi.NumHAcceptors(mol) <= 10 and \
Lipinksi.rdMolDescriptors.CalcExactMolWt(mol) < 500 and \
Lipinksi.rdMolDescriptors.CalcCrippenDescriptors(mol)[0] <= 5
filtered = []
for comp in compounds[:1500]: # рассмотрим только часть, потому что их и так много
if "N=[N+]=[N-]" in comp['CanonicalSMILES']:
newcomp=comp['CanonicalSMILES'].replace('N=[N+]=[N-]', ibu_template)
else:
continue
# Новую молекулу лучше создавать в try из-за битых Smiles
try:
newmol=Chem.MolFromSmiles(newcomp)
if pass_Lipinksi(newmol):
filtered.append(newmol)
except:
pass
len(filtered)
292
Визуализируем 50 случайных из них:
display(Draw.MolsToGridImage(np.random.choice(filtered, 50), useSVG=False, molsPerRow=5, subImgSize=(200, 200)))
В целом, выглядит похоже на то, что мы хотели получить: ибупрофен, клик-цикл через эфир, новый фрагмент. Можно идти запускать тысячу докингов:)
Нарисуем карту сходства ибупрофена и одного из построенных веществ.
fig, maxweight = SimilarityMaps.GetSimilarityMapForFingerprint(ibu, filtered[4], SimilarityMaps.GetMorganFingerprint)
Ожидаемо, на карте сходства хорошо различима подструктура оригинального ибупрофена (зелёный цвет — высокое сходство) и добавленный новый фрагмент (красный цвет — низкое сходство).
Для сравнения посмотрим на молекулу, содержащую две подструктуры ибупрофена
fig, maxweight = SimilarityMaps.GetSimilarityMapForFingerprint(ibu,
Chem.MolFromSmiles('CC(C)CC1=CC=C(C=C1)C(C)C(=O)OCC(C)CC1=CC=C(C=C1)C(C)C(=O)O'),
SimilarityMaps.GetMorganFingerprint)
Обе подструктуры покрашены зелёным, а новосозданная эфирная связь красным — сходство оценивается именно по совпадению фингерпринтов, а не "наложением" атомов молекул друг на друга.
Не работает
m3d=Chem.AddHs(ibu)
Chem.AllChem.EmbedMolecule(m3d)
AllChem.MMFFOptimizeMolecule(m3d,maxIters=500,nonBondedThresh=200)
0
import nglview as nv
widget = nv.show_rdkit(m3d)
widget # пропадает при экспорте
NGLWidget()
widget.render_image(0) # тоже пропадает при экспорте
Image(value=b'', width='99%')
widget.download_image('ibu.png')
Image('ibu.png')