Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,220 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from e_smiles import *
|
2 |
+
import gradio as gr
|
3 |
+
from rdkit import Chem
|
4 |
+
from rdkit.Chem import Draw
|
5 |
+
import os
|
6 |
+
|
7 |
+
def remove_atom_mapping_and_isotopes(smiles):
|
8 |
+
mol = Chem.MolFromSmiles(smiles)
|
9 |
+
|
10 |
+
if mol is None:
|
11 |
+
raise ValueError("Invalid SMILES string")
|
12 |
+
|
13 |
+
for atom in mol.GetAtoms():
|
14 |
+
atom.SetIsotope(0)
|
15 |
+
atom.SetAtomMapNum(0)
|
16 |
+
|
17 |
+
clean_smiles = Chem.MolToSmiles(Chem.RemoveHs(mol))
|
18 |
+
return clean_smiles
|
19 |
+
|
20 |
+
def get_bondtype_and_convert2string(mol, atom1_idx, atom2_idx):
|
21 |
+
atom1_idx = int(atom1_idx)
|
22 |
+
atom2_idx = int(atom2_idx)
|
23 |
+
bond = mol.GetBondBetweenAtoms(atom1_idx - 1, atom2_idx - 1)
|
24 |
+
if bond is None:
|
25 |
+
raise ValueError("Atoms are not bonded")
|
26 |
+
|
27 |
+
if bond.GetBondType() == Chem.BondType.SINGLE:
|
28 |
+
return '1.0'
|
29 |
+
elif bond.GetBondType() == Chem.BondType.DOUBLE:
|
30 |
+
return '2.0'
|
31 |
+
elif bond.GetBondType() == Chem.BondType.TRIPLE:
|
32 |
+
return '3.0'
|
33 |
+
else:
|
34 |
+
raise ValueError("Unsupported bond type")
|
35 |
+
|
36 |
+
def main_interface(smiles):
|
37 |
+
img, mapped_smiles, secondary_inputs_visible1, secondary_inputs_visible2, error = process_smiles(smiles)
|
38 |
+
return img, mapped_smiles, gr.update(visible=True), secondary_inputs_visible2, error
|
39 |
+
|
40 |
+
def process_smiles(smiles):
|
41 |
+
try:
|
42 |
+
smiles = remove_atom_mapping_and_isotopes(smiles)
|
43 |
+
mol = Chem.MolFromSmiles(smiles)
|
44 |
+
if mol:
|
45 |
+
Chem.Kekulize(mol, clearAromaticFlags=True)
|
46 |
+
for atom in mol.GetAtoms():
|
47 |
+
atom.SetAtomMapNum(atom.GetIdx() + 1)
|
48 |
+
|
49 |
+
mapped_smiles = Chem.MolToSmiles(mol, canonical=False, kekuleSmiles=True)
|
50 |
+
else:
|
51 |
+
raise ValueError("Invalid SMILES string")
|
52 |
+
|
53 |
+
mol = Chem.MolFromSmiles(mapped_smiles)
|
54 |
+
img = Draw.MolToImage(mol, size=(450, 450))
|
55 |
+
return img, mapped_smiles, gr.update(visible=True), gr.update(visible=True), gr.update(visible=False)
|
56 |
+
except Exception as e:
|
57 |
+
return None, None, gr.update(visible=False), gr.update(visible=False), gr.update(visible=True, value=str(e))
|
58 |
+
|
59 |
+
def process_bond_operation(smiles, atom1_idx, atom2_idx, operation):
|
60 |
+
try:
|
61 |
+
mol = Chem.MolFromSmiles(smiles)
|
62 |
+
if mol is None:
|
63 |
+
raise ValueError("Invalid SMILES string")
|
64 |
+
|
65 |
+
if atom1_idx > atom2_idx:
|
66 |
+
atom1_idx, atom2_idx = atom2_idx, atom1_idx
|
67 |
+
|
68 |
+
first = str(atom1_idx)
|
69 |
+
second = str(atom2_idx)
|
70 |
+
third = get_bondtype_and_convert2string(mol, atom1_idx, atom2_idx)
|
71 |
+
|
72 |
+
if operation == "break this bond":
|
73 |
+
prompt = [first + ':' + second + ':' + third + ':' + '0.0']
|
74 |
+
elif operation == "change this bond to single bond":
|
75 |
+
prompt = [first + ':' + second + ':' + third + ':' + '1.0']
|
76 |
+
elif operation == "change this bond to double bond":
|
77 |
+
prompt = [first + ':' + second + ':' + third + ':' + '2.0']
|
78 |
+
elif operation == "change this bond to triple bond":
|
79 |
+
prompt = [first + ':' + second + ':' + third + ':' + '3.0']
|
80 |
+
else:
|
81 |
+
raise ValueError("Unsupported operation")
|
82 |
+
|
83 |
+
prompt = get_b_smiles_check([smiles, prompt, [], [], [], [], []])
|
84 |
+
|
85 |
+
return prompt, gr.update(visible=False)
|
86 |
+
except Exception as e:
|
87 |
+
return None, gr.update(visible=True, value=str(e))
|
88 |
+
|
89 |
+
def retrosynthesis(reactant, prompt):
|
90 |
+
# 1. generate src file
|
91 |
+
with open('tmp_data/src.txt', 'w') as f:
|
92 |
+
f.write(" ".join(prompt))
|
93 |
+
|
94 |
+
# 2. inference
|
95 |
+
os.system('bash infer.sh')
|
96 |
+
|
97 |
+
|
98 |
+
# 3. readout reactseq
|
99 |
+
with open('./tmp_data/tgt.txt', 'r') as f:
|
100 |
+
lines = f.readlines()
|
101 |
+
|
102 |
+
reactseq_list = []
|
103 |
+
for line in lines:
|
104 |
+
line = line.replace(" ", "").rstrip("\n")
|
105 |
+
reactseq_list.append(reactant + ">>>" + line)
|
106 |
+
|
107 |
+
# 4. merge reactseq
|
108 |
+
reactant_smiles = []
|
109 |
+
for seq in reactseq_list:
|
110 |
+
reactant_smiles.append(merge_smiles_with_mapping(seq))
|
111 |
+
|
112 |
+
# 5. draw reactants
|
113 |
+
reactants = []
|
114 |
+
reactant_images = []
|
115 |
+
for smiles in reactant_smiles:
|
116 |
+
mol = Chem.MolFromSmiles(smiles)
|
117 |
+
reactants.append(smiles)
|
118 |
+
reactant_images.append(Draw.MolToImage(mol, size=(600, 600)))
|
119 |
+
|
120 |
+
return reactants, reactant_images
|
121 |
+
|
122 |
+
def secondary_interface(smiles, atom1_idx, atom2_idx, operation):
|
123 |
+
try:
|
124 |
+
prompt, error = process_bond_operation(smiles, atom1_idx, atom2_idx, operation)
|
125 |
+
|
126 |
+
if prompt:
|
127 |
+
reactant_smiles, reactant_images = retrosynthesis(smiles, prompt)
|
128 |
+
output_components = [gr.update(value=prompt, visible=True)] # prompt_output
|
129 |
+
|
130 |
+
# Add the reactant SMILES and images
|
131 |
+
for i in range(len(reactant_smiles)):
|
132 |
+
output_components.append(gr.update(value=reactant_smiles[i], visible=True))
|
133 |
+
output_components.append(gr.update(value=reactant_images[i], visible=True))
|
134 |
+
|
135 |
+
# Fill remaining hidden outputs
|
136 |
+
for j in range(len(reactant_smiles), 10):
|
137 |
+
output_components.append(gr.update(visible=False))
|
138 |
+
output_components.append(gr.update(visible=False))
|
139 |
+
|
140 |
+
output_components.append(gr.update(visible=False)) # error_output
|
141 |
+
return output_components
|
142 |
+
|
143 |
+
else:
|
144 |
+
return [gr.update(visible=False)] * 21 + [error] # Assuming 10 pairs of outputs + prompt_output + error_output
|
145 |
+
|
146 |
+
except Exception as e:
|
147 |
+
return [gr.update(visible=False)] * 21 + [gr.update(visible=True, value=str(e))]
|
148 |
+
|
149 |
+
def clear_interface():
|
150 |
+
return (
|
151 |
+
gr.update(value=None), # img_output
|
152 |
+
gr.update(value=None), # smiles_output
|
153 |
+
gr.update(visible=False), # secondary_inputs
|
154 |
+
gr.update(visible=False), # secondary_inputs
|
155 |
+
gr.update(visible=False, value=None), # error_output
|
156 |
+
gr.update(value=None, visible=False), # prompt_output
|
157 |
+
) + ( # Reactant SMILES and Images
|
158 |
+
tuple(gr.update(value=None, visible=False) for _ in range(20))
|
159 |
+
)
|
160 |
+
|
161 |
+
# 示例数据
|
162 |
+
examples = [
|
163 |
+
["N#CC1=CC=C(C(N2N=CN=C2)C(O)CC3=CC=C(F)C=C3)C=C1", 7, 18, "break this bond"],
|
164 |
+
["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"],
|
165 |
+
["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"],
|
166 |
+
]
|
167 |
+
|
168 |
+
with gr.Blocks() as interface:
|
169 |
+
gr.Markdown("# ReactSeq")
|
170 |
+
gr.Markdown("Please input a SMILES string and two atom indices between the bond which you want to perform an operation.")
|
171 |
+
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.")
|
172 |
+
gr.Markdown("The molecule image and SMILES with atom mapping will be displayed.")
|
173 |
+
gr.Markdown("After you input the operation, the prompt will be displayed.")
|
174 |
+
gr.Markdown("The reactants SMILES and images will be displayed after the retrosynthesis with **default** ranking.")
|
175 |
+
|
176 |
+
smiles_input = gr.Textbox(placeholder="Please input your SMILES string", label="SMILES input")
|
177 |
+
submit_smiles_button = gr.Button("Submit SMILES")
|
178 |
+
|
179 |
+
gr.Markdown("The molecule image and SMILES with atom mapping:")
|
180 |
+
img_output = gr.Image(label="Molecule image")
|
181 |
+
smiles_output = gr.Textbox(label="SMILES with atom mapping", interactive=False)
|
182 |
+
|
183 |
+
with gr.Row(visible=False) as secondary_inputs:
|
184 |
+
atom1_idx = gr.Number(label="The first atom index", minimum=1)
|
185 |
+
atom2_idx = gr.Number(label="The second atom index", minimum=2)
|
186 |
+
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")
|
187 |
+
submit_operation_button = gr.Button("Submit Operation")
|
188 |
+
|
189 |
+
prompt_output = gr.Textbox(label="Prompt", interactive=False, visible=False)
|
190 |
+
|
191 |
+
result_row = []
|
192 |
+
for i in range(10):
|
193 |
+
result_row.append(gr.Textbox(label=f"Reactant {i+1} SMILES", interactive=False, visible=False))
|
194 |
+
result_row.append(gr.Image(label=f"Reactant {i+1} Image", visible=False))
|
195 |
+
|
196 |
+
error_output = gr.Textbox(label="Error Message", interactive=False, visible=False)
|
197 |
+
|
198 |
+
# 添加清除按钮
|
199 |
+
clear_button = gr.Button("Clear")
|
200 |
+
|
201 |
+
submit_smiles_button.click(main_interface, inputs=[smiles_input], outputs=[img_output, smiles_output, secondary_inputs, secondary_inputs, error_output])
|
202 |
+
submit_operation_button.click(
|
203 |
+
secondary_interface,
|
204 |
+
inputs=[smiles_output, atom1_idx, atom2_idx, operation],
|
205 |
+
outputs=[prompt_output] + result_row + [error_output]
|
206 |
+
)
|
207 |
+
|
208 |
+
# 清除按钮的事件处理
|
209 |
+
clear_button.click(
|
210 |
+
clear_interface,
|
211 |
+
inputs=[],
|
212 |
+
outputs=[img_output, smiles_output, secondary_inputs, secondary_inputs, error_output, prompt_output] + result_row
|
213 |
+
)
|
214 |
+
|
215 |
+
gr.Examples(examples=examples, inputs=[smiles_input, atom1_idx, atom2_idx, operation])
|
216 |
+
|
217 |
+
interface.launch()
|
218 |
+
|
219 |
+
|
220 |
+
|