tagirshin commited on
Commit
8bfdef1
·
1 Parent(s): 109f975
Files changed (6) hide show
  1. .gitignore +145 -0
  2. .streamlit/conf.toml +2 -0
  3. README.md +10 -5
  4. app.py +263 -0
  5. pre-requirements.txt +2 -0
  6. requirements.txt +3 -0
.gitignore ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ pip-wheel-metadata/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+
30
+ # PyInstaller
31
+ # Usually these files are written by a python script from a template
32
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
33
+ *.manifest
34
+ *.spec
35
+
36
+ # Installer logs
37
+ pip-log.txt
38
+ pip-delete-this-directory.txt
39
+
40
+ # Unit test / coverage reports
41
+ htmlcov/
42
+ .tox/
43
+ .nox/
44
+ .coverage
45
+ .coverage.*
46
+ .cache
47
+ nosetests.xml
48
+ coverage.xml
49
+ *.cover
50
+ *.py,cover
51
+ .hypothesis/
52
+ .pytest_cache/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/build/
73
+ docs/_build/
74
+ docs/modules.rst
75
+ docs/cli_help.txt
76
+
77
+ # PyBuilder
78
+ target/
79
+
80
+ # Jupyter Notebook
81
+ .ipynb_checkpoints
82
+
83
+ # IPython
84
+ profile_default/
85
+ ipython_config.py
86
+
87
+ # pyenv
88
+ .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
98
+ __pypackages__/
99
+
100
+ # Celery stuff
101
+ celerybeat-schedule
102
+ celerybeat.pid
103
+
104
+ # SageMath parsed files
105
+ *.sage.py
106
+
107
+ # Environments
108
+ .env
109
+ .venv
110
+ env/
111
+ venv/
112
+ ENV/
113
+ env.bak/
114
+ venv.bak/
115
+
116
+ # Spyder project settings
117
+ .spyderproject
118
+ .spyproject
119
+
120
+ # Rope project settings
121
+ .ropeproject
122
+
123
+ # mkdocs documentation
124
+ /site
125
+
126
+ # mypy
127
+ .mypy_cache/
128
+ .dmypy.json
129
+ dmypy.json
130
+
131
+ # Pyre type checker
132
+ .pyre/
133
+
134
+ # PyCharmm idea
135
+ .idea/
136
+
137
+ #VS Code
138
+ .vscode/
139
+
140
+ # Pytest coverage output
141
+ coverage/
142
+
143
+ .DS_Store
144
+ .AppleDouble
145
+ .LSOverride
.streamlit/conf.toml ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ [theme]
2
+ base="light"
README.md CHANGED
@@ -1,12 +1,17 @@
1
  ---
2
- title: SynPlanner
3
- emoji: 🐠
4
- colorFrom: blue
5
- colorTo: purple
6
  sdk: streamlit
7
  sdk_version: 1.37.0
8
  app_file: app.py
9
  pinned: false
 
 
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
1
  ---
2
+ title: SynPlanner GUI
3
+ emoji: 🧪
4
+ colorFrom: pink
5
+ colorTo: blue
6
  sdk: streamlit
7
  sdk_version: 1.37.0
8
  app_file: app.py
9
  pinned: false
10
+ license: mit
11
+ python_version: 3.11.9
12
  ---
13
 
14
+ # SynPlanner Graphical User Interface (GUI)
15
+ Try the GUI to find reaction paths...
16
+
17
+ **documentation to be done**
app.py ADDED
@@ -0,0 +1,263 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import pickle
3
+ import re
4
+ import uuid
5
+
6
+ import pandas as pd
7
+ import streamlit as st
8
+ from CGRtools.files import SMILESRead
9
+ from streamlit_ketcher import st_ketcher
10
+ from huggingface_hub import hf_hub_download
11
+ from huggingface_hub.utils import disable_progress_bars
12
+
13
+
14
+ from synplan.mcts.expansion import PolicyNetworkFunction
15
+ from synplan.mcts.search import extract_tree_stats
16
+ from synplan.mcts.tree import Tree
17
+ from synplan.chem.utils import mol_from_smiles
18
+ from synplan.utils.config import TreeConfig, PolicyNetworkConfig
19
+ from synplan.utils.loading import load_reaction_rules, load_building_blocks
20
+ from synplan.utils.visualisation import generate_results_html, get_route_svg
21
+
22
+ disable_progress_bars("huggingface_hub")
23
+
24
+ smiles_parser = SMILESRead.create_parser(ignore=True)
25
+
26
+
27
+ def download_button(object_to_download, download_filename, button_text, pickle_it=False):
28
+ """
29
+ Issued from
30
+ Generates a link to download the given object_to_download.
31
+ Params:
32
+ ------
33
+ object_to_download: The object to be downloaded.
34
+ download_filename (str): filename and extension of file. e.g. mydata.csv,
35
+ some_txt_output.txt download_link_text (str): Text to display for download
36
+ link.
37
+ button_text (str): Text to display on download button (e.g. 'click here to download file')
38
+ pickle_it (bool): If True, pickle file.
39
+ Returns:
40
+ -------
41
+ (str): the anchor tag to download object_to_download
42
+ Examples:
43
+ --------
44
+ download_link(your_df, 'YOUR_DF.csv', 'Click to download data!')
45
+ download_link(your_str, 'YOUR_STRING.txt', 'Click to download text!')
46
+ """
47
+ if pickle_it:
48
+ try:
49
+ object_to_download = pickle.dumps(object_to_download)
50
+ except pickle.PicklingError as e:
51
+ st.write(e)
52
+ return None
53
+
54
+ else:
55
+ if isinstance(object_to_download, bytes):
56
+ pass
57
+
58
+ elif isinstance(object_to_download, pd.DataFrame):
59
+ object_to_download = object_to_download.to_csv(index=False).encode('utf-8')
60
+
61
+ # Try JSON encode for everything else # else: # object_to_download = json.dumps(object_to_download)
62
+
63
+ try:
64
+ # some strings <-> bytes conversions necessary here
65
+ b64 = base64.b64encode(object_to_download.encode()).decode()
66
+
67
+ except AttributeError:
68
+ b64 = base64.b64encode(object_to_download).decode()
69
+
70
+ button_uuid = str(uuid.uuid4()).replace('-', '')
71
+ button_id = re.sub('\d+', '', button_uuid)
72
+
73
+ custom_css = f"""
74
+ <style>
75
+ #{button_id} {{
76
+ background-color: rgb(255, 255, 255);
77
+ color: rgb(38, 39, 48);
78
+ text-decoration: none;
79
+ border-radius: 4px;
80
+ border-width: 1px;
81
+ border-style: solid;
82
+ border-color: rgb(230, 234, 241);
83
+ border-image: initial;
84
+ }}
85
+ #{button_id}:hover {{
86
+ border-color: rgb(246, 51, 102);
87
+ color: rgb(246, 51, 102);
88
+ }}
89
+ #{button_id}:active {{
90
+ box-shadow: none;
91
+ background-color: rgb(246, 51, 102);
92
+ color: white;
93
+ }}
94
+ </style> """
95
+
96
+ dl_link = custom_css + f'<a download="{download_filename}" id="{button_id}" href="data:file/txt;base64,{b64}">{button_text}</a><br></br>'
97
+
98
+ return dl_link
99
+
100
+
101
+ st.set_page_config(page_title="SynPlanner GUI", page_icon="🧪", layout="wide")
102
+
103
+ intro_text = '''
104
+ This is a demo of the graphical user interface of
105
+ [SynPlanner](https://github.com/Laboratoire-de-Chemoinformatique/SynPlanner/).
106
+ SynPlanner is a comprehensive tool for reaction data curation, rule extraction, model training and retrosynthetic planning.
107
+
108
+ More information on SynPlanner is available in the [official docs](https://synplanner.readthedocs.io/en/latest/index.html).
109
+ '''
110
+
111
+ st.title("`SynPlanner GUI`")
112
+
113
+ st.write(intro_text)
114
+
115
+ st.header('Molecule input')
116
+ st.markdown(
117
+ '''
118
+ You can provide a molecular structure by either providing:
119
+ * SMILES string + Enter
120
+ * Draw it + Apply
121
+ '''
122
+ )
123
+
124
+ DEFAULT_MOL = 'c1cc(ccc1Cl)C(CCO)NC(C2(CCN(CC2)c3c4cc[nH]c4ncn3)N)=O'
125
+ molecule = st.text_input("SMILES:", DEFAULT_MOL)
126
+ smile_code = st_ketcher(molecule)
127
+ target_molecule = mol_from_smiles(smile_code)
128
+
129
+ building_blocks_path = hf_hub_download(
130
+ repo_id="Laboratoire-De-Chemoinformatique/SynPlanner",
131
+ filename="building_blocks_em_sa_ln.smi",
132
+ subfolder="building_blocks",
133
+ local_dir="."
134
+ )
135
+
136
+ ranking_policy_weights_path = hf_hub_download(
137
+ repo_id="Laboratoire-De-Chemoinformatique/SynPlanner",
138
+ filename="ranking_policy_network.ckpt",
139
+ subfolder="uspto/weights",
140
+ local_dir="."
141
+ )
142
+
143
+ reaction_rules_path = hf_hub_download(
144
+ repo_id="Laboratoire-De-Chemoinformatique/SynPlanner",
145
+ filename="uspto_reaction_rules.pickle",
146
+ subfolder="uspto",
147
+ local_dir="."
148
+ )
149
+
150
+ st.header('Launch calculation')
151
+ st.markdown(
152
+ '''If you modified the structure, please ensure you clicked on `Apply` (bottom right of the molecular editor).'''
153
+ )
154
+ st.markdown(f"The molecule SMILES is actually: ``{smile_code}``")
155
+
156
+ st.subheader('Planning options')
157
+
158
+ st.markdown(
159
+ '''
160
+ The description of each option can be found in the
161
+ [Retrosynthetic Planning Tutorial](https://synplanner.readthedocs.io/en/latest/tutorial_files/retrosynthetic_planning.html#Configuring-search-tree).
162
+ '''
163
+ )
164
+
165
+ col_options_1, col_options_2 = st.columns(2, gap="medium")
166
+
167
+ with col_options_1:
168
+ search_strategy_input = st.selectbox(label='Search strategy', options=('Expansion first', 'Evaluation first',), index=0)
169
+ ucb_type = st.selectbox(label='Search strategy', options=('uct', 'puct', 'value'), index=0)
170
+ c_ucb = st.number_input("C coefficient of UCB", value=0.1, placeholder="Type a number...")
171
+
172
+ with col_options_2:
173
+ max_iterations = st.slider('Total number of MCTS iterations', min_value=50, max_value=300, value=100)
174
+ max_depth = st.slider('Maximal number of reaction steps', min_value=3, max_value=9, value=6)
175
+ min_mol_size = st.slider('Minimum size of a molecule to be precursor', min_value=0, max_value=7, value=0)
176
+
177
+ search_strategy_translator = {
178
+ "Expansion first": "expansion_first",
179
+ "Evaluation first": "evaluation_first",
180
+ }
181
+ search_strategy = search_strategy_translator[search_strategy_input]
182
+
183
+ submit_planning = st.button('Start retrosynthetic planning')
184
+
185
+ if submit_planning:
186
+ with st.status("Downloading data"):
187
+ st.write("Downloading building blocks")
188
+ building_blocks = load_building_blocks(building_blocks_path, standardize=False)
189
+ st.write('Downloading reaction rules')
190
+ reaction_rules = load_reaction_rules(reaction_rules_path)
191
+ st.write('Loading policy network')
192
+ policy_config = PolicyNetworkConfig(weights_path=ranking_policy_weights_path)
193
+ policy_function = PolicyNetworkFunction(policy_config=policy_config)
194
+
195
+ tree_config = TreeConfig(
196
+ search_strategy=search_strategy,
197
+ evaluation_type="rollout",
198
+ max_iterations=max_iterations,
199
+ max_depth=max_depth,
200
+ min_mol_size=min_mol_size,
201
+ init_node_value=0.5,
202
+ ucb_type=ucb_type,
203
+ c_ucb=c_ucb,
204
+ silent=True
205
+ )
206
+
207
+ tree = Tree(
208
+ target=target_molecule,
209
+ config=tree_config,
210
+ reaction_rules=reaction_rules,
211
+ building_blocks=building_blocks,
212
+ expansion_function=policy_function,
213
+ evaluation_function=None,
214
+ )
215
+
216
+ mcts_progress_text = "Running retrosynthetic planning"
217
+ mcts_bar = st.progress(0, text=mcts_progress_text)
218
+ for step, (solved, node_id) in enumerate(tree):
219
+ mcts_bar.progress(step / max_iterations, text=mcts_progress_text)
220
+
221
+ res = extract_tree_stats(tree, target_molecule)
222
+
223
+ st.header('Results')
224
+ if res["solved"]:
225
+ st.balloons()
226
+
227
+ st.subheader("Examples of found retrosynthetic routes")
228
+
229
+ image_counter = 0
230
+ visualised_node_ids = set()
231
+ for n, node_id in enumerate(sorted(set(tree.winning_nodes))):
232
+ if image_counter == 3:
233
+ break
234
+ if n % 2 == 0 and node_id not in visualised_node_ids:
235
+ visualised_node_ids.add(node_id)
236
+ image_counter += 1
237
+ num_steps = len(tree.synthesis_route(node_id))
238
+ route_score = round(tree.route_score(node_id), 3)
239
+ st.image(get_route_svg(tree, node_id), caption=f"Route {node_id}; {num_steps} steps; Route score: {route_score}")
240
+
241
+ stat_col, download_col = st.columns(2, gap="medium")
242
+
243
+ with stat_col:
244
+ st.subheader("Statistics")
245
+ df = pd.DataFrame(res, index=[0])
246
+ st.write(df[["target_smiles", "num_routes", "num_nodes", "num_iter", "search_time"]])
247
+
248
+ with download_col:
249
+ st.subheader("Downloads")
250
+ html_body = generate_results_html(tree, html_path=None, extended=True)
251
+ dl_html = download_button(html_body, 'results_synplanner.html', 'Download results as a HTML file')
252
+
253
+ dl_csv = download_button(pd.DataFrame(res, index=[0]), 'results_synplanner.csv',
254
+ 'Download statistics as a csv file')
255
+ st.markdown(dl_html + dl_csv, unsafe_allow_html=True)
256
+
257
+ else:
258
+ st.write("Found no reaction path.")
259
+
260
+ st.divider()
261
+ st.header('Restart from the beginning?')
262
+ if st.button("Restart"):
263
+ st.rerun()
pre-requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ --find-links https://download.pytorch.org/whl/torch_stable.html
2
+ torch==2.2.2+cpu
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ streamlit
2
+ streamlit_ketcher
3
+ git+https://github.com/Laboratoire-de-Chemoinformatique/SynPlanner.git