|
from e_smiles import * |
|
import gradio as gr |
|
from rdkit import Chem |
|
from rdkit.Chem import Draw |
|
import os |
|
|
|
|
|
def remove_atom_mapping_and_isotopes(smiles): |
|
mol = Chem.MolFromSmiles(smiles) |
|
|
|
if mol is None: |
|
raise ValueError("Invalid SMILES string") |
|
|
|
for atom in mol.GetAtoms(): |
|
atom.SetIsotope(0) |
|
atom.SetAtomMapNum(0) |
|
|
|
clean_smiles = Chem.MolToSmiles(Chem.RemoveHs(mol)) |
|
return clean_smiles |
|
|
|
def get_bondtype_and_convert2string(mol, atom1_idx, atom2_idx): |
|
atom1_idx = int(atom1_idx) |
|
atom2_idx = int(atom2_idx) |
|
bond = mol.GetBondBetweenAtoms(atom1_idx - 1, atom2_idx - 1) |
|
if bond is None: |
|
raise ValueError("Atoms are not bonded") |
|
|
|
if bond.GetBondType() == Chem.BondType.SINGLE: |
|
return '1.0' |
|
elif bond.GetBondType() == Chem.BondType.DOUBLE: |
|
return '2.0' |
|
elif bond.GetBondType() == Chem.BondType.TRIPLE: |
|
return '3.0' |
|
else: |
|
raise ValueError("Unsupported bond type") |
|
|
|
def main_interface(smiles): |
|
img, mapped_smiles, secondary_inputs_visible1, secondary_inputs_visible2, error = process_smiles(smiles) |
|
return img, mapped_smiles, gr.update(visible=True), secondary_inputs_visible2, error |
|
|
|
def process_smiles(smiles): |
|
try: |
|
smiles = remove_atom_mapping_and_isotopes(smiles) |
|
mol = Chem.MolFromSmiles(smiles) |
|
if mol: |
|
Chem.Kekulize(mol, clearAromaticFlags=True) |
|
for atom in mol.GetAtoms(): |
|
atom.SetAtomMapNum(atom.GetIdx() + 1) |
|
|
|
mapped_smiles = Chem.MolToSmiles(mol, canonical=False, kekuleSmiles=True) |
|
else: |
|
raise ValueError("Invalid SMILES string") |
|
|
|
mol = Chem.MolFromSmiles(mapped_smiles) |
|
img = Draw.MolToImage(mol, size=(450, 450)) |
|
return img, mapped_smiles, gr.update(visible=True), gr.update(visible=True), gr.update(visible=False) |
|
except Exception as e: |
|
return None, None, gr.update(visible=False), gr.update(visible=False), gr.update(visible=True, value=str(e)) |
|
|
|
def process_bond_operation(smiles, atom1_idx, atom2_idx, operation): |
|
try: |
|
mol = Chem.MolFromSmiles(smiles) |
|
if mol is None: |
|
raise ValueError("Invalid SMILES string") |
|
|
|
if atom1_idx > atom2_idx: |
|
atom1_idx, atom2_idx = atom2_idx, atom1_idx |
|
|
|
first = str(atom1_idx) |
|
second = str(atom2_idx) |
|
third = get_bondtype_and_convert2string(mol, atom1_idx, atom2_idx) |
|
|
|
if operation == "break this bond": |
|
prompt = [first + ':' + second + ':' + third + ':' + '0.0'] |
|
elif operation == "change this bond to single bond": |
|
prompt = [first + ':' + second + ':' + third + ':' + '1.0'] |
|
elif operation == "change this bond to double bond": |
|
prompt = [first + ':' + second + ':' + third + ':' + '2.0'] |
|
elif operation == "change this bond to triple bond": |
|
prompt = [first + ':' + second + ':' + third + ':' + '3.0'] |
|
else: |
|
raise ValueError("Unsupported operation") |
|
|
|
prompt = get_b_smiles_check([smiles, prompt, [], [], [], [], []]) |
|
|
|
return prompt, gr.update(visible=False) |
|
except Exception as e: |
|
return None, gr.update(visible=True, value=str(e)) |
|
|
|
def retrosynthesis(reactant, prompt): |
|
|
|
with open('tmp_data/src.txt', 'w') as f: |
|
f.write(" ".join(prompt)) |
|
|
|
|
|
os.system('bash infer.sh') |
|
|
|
|
|
|
|
with open('./tmp_data/tgt.txt', 'r') as f: |
|
lines = f.readlines() |
|
|
|
reactseq_list = [] |
|
reactant_smiles = [] |
|
for line in lines: |
|
line = line.replace(" ", "").rstrip("\n") |
|
reactseq_list.append(line) |
|
rxn = reactant + ">>>" + line |
|
|
|
|
|
reactant_smiles.append(merge_smiles_with_mapping(rxn)) |
|
|
|
|
|
reactants = [] |
|
reactant_images = [] |
|
for smiles in reactant_smiles: |
|
mol = Chem.MolFromSmiles(smiles) |
|
reactants.append(smiles) |
|
reactant_images.append(Draw.MolToImage(mol, size=(600, 600))) |
|
|
|
return reactseq_list, reactants, reactant_images |
|
|
|
prompt_output = gr.Textbox(label="Prompt", interactive=False, visible=True) |
|
|
|
def secondary_interface(smiles, atom1_idx, atom2_idx, operation, progress=gr.Progress()): |
|
try: |
|
prompt, error = process_bond_operation(smiles, atom1_idx, atom2_idx, operation) |
|
|
|
if prompt: |
|
reactseq_list, reactant_smiles, reactant_images = retrosynthesis(smiles, prompt) |
|
output_components = [gr.update(value=prompt)] |
|
|
|
for i in range(len(reactant_smiles)): |
|
if reactant_smiles[i] != "": |
|
output_components.append(gr.update(value=reactseq_list[i], visible=True)) |
|
output_components.append(gr.update(value=reactant_smiles[i], visible=True)) |
|
output_components.append(gr.update(value=reactant_images[i], visible=True)) |
|
else: |
|
output_components.append(gr.update(visible=False)) |
|
output_components.append(gr.update(visible=False)) |
|
output_components.append(gr.update(visible=False)) |
|
|
|
output_components.append(gr.update(visible=False)) |
|
return output_components |
|
|
|
else: |
|
return [gr.update(visible=False)] * 31 + [error] |
|
|
|
except Exception as e: |
|
return [gr.update(visible=False)] * 31 + [gr.update(visible=True, value=str(e))] |
|
|
|
|
|
|
|
def clear_interface(): |
|
return ( |
|
gr.update(value=None), |
|
gr.update(value=None), |
|
gr.update(value=None), |
|
gr.update(visible=False), |
|
gr.update(visible=False), |
|
gr.update(visible=False, value=None), |
|
gr.update(value=None, visible=True), |
|
) + ( |
|
tuple(gr.update(value=None, visible=False) for _ in range(30)) |
|
) |
|
|
|
|
|
examples = [ |
|
["N#CC1=CC=C(C(N2N=CN=C2)C(O)CC3=CC=C(F)C=C3)C=C1", 7, 18, "break this bond"], |
|
["N#CC1=CC=C(C(N2N=CN=C2)C(O)CC3=CC=C(F)C=C3)C=C1", 8, 9, "change this bond to double bond"], |
|
["N#CC1=CC=C(C(N2N=CN=C2)C(O)CC3=CC=C(F)C=C3)C=C1", 1, 2, "change this bond to single bond"], |
|
] |
|
|
|
with gr.Blocks() as interface: |
|
gr.Markdown("# ReactSeq") |
|
gr.Markdown("Please input a SMILES string and two atom indices between the bond which you want to perform an operation.") |
|
gr.Markdown("The operation can be one of the following: break this bond, change this bond to single bond, change this bond to double bond, change this bond to triple bond.") |
|
gr.Markdown("The molecule image and SMILES with atom mapping will be displayed.") |
|
gr.Markdown("After you input the operation, the prompt will be displayed.") |
|
gr.Markdown("The reactants SMILES and images will be displayed after the retrosynthesis with **default** ranking, with the invalid ones deleted.") |
|
|
|
smiles_input = gr.Textbox(placeholder="Please input your SMILES string", label="SMILES input") |
|
submit_smiles_button = gr.Button("Submit SMILES") |
|
|
|
gr.Markdown("The molecule image and SMILES with atom mapping:") |
|
img_output = gr.Image(label="Molecule image") |
|
smiles_output = gr.Textbox(label="SMILES with atom mapping", interactive=False) |
|
|
|
with gr.Row(visible=False) as secondary_inputs: |
|
atom1_idx = gr.Number(label="The first atom index", minimum=1) |
|
atom2_idx = gr.Number(label="The second atom index", minimum=1) |
|
operation = gr.Dropdown(choices=["break this bond", "change this bond to single bond", "change this bond to double bond", "change this bond to triple bond"], label="Operation") |
|
submit_operation_button = gr.Button("Submit Operation") |
|
|
|
prompt_output = gr.Textbox(label="Prompt", interactive=False, visible=True) |
|
|
|
result_row = [] |
|
for i in range(10): |
|
result_row.append(gr.Textbox(label=f"Reactseq {i+1} ", interactive=False, visible=False)) |
|
result_row.append(gr.Textbox(label=f"Reactant {i+1} SMILES", interactive=False, visible=False)) |
|
result_row.append(gr.Image(label=f"Reactant {i+1} Image", visible=False)) |
|
|
|
error_output = gr.Textbox(label="Error Message", interactive=False, visible=False) |
|
|
|
clear_button = gr.Button("Clear") |
|
|
|
submit_smiles_button.click(main_interface, inputs=[smiles_input], outputs=[img_output, smiles_output, secondary_inputs, secondary_inputs, error_output]) |
|
submit_operation_button.click( |
|
secondary_interface, |
|
inputs=[smiles_output, atom1_idx, atom2_idx, operation], |
|
outputs=[prompt_output] + result_row + [error_output] |
|
) |
|
|
|
clear_button.click( |
|
clear_interface, |
|
inputs=[], |
|
outputs=[smiles_input, img_output, smiles_output, secondary_inputs, secondary_inputs, error_output, prompt_output] + result_row |
|
) |
|
|
|
gr.Examples(examples=examples, inputs=[smiles_input, atom1_idx, atom2_idx, operation]) |
|
|
|
interface.launch() |
|
|
|
|
|
|
|
|