shreyab21 commited on
Commit
7ab1cf6
·
verified ·
1 Parent(s): 0504b62

Create src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +237 -0
src/streamlit_app.py ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # dashboard.py
2
+ import os
3
+ os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
4
+
5
+ import streamlit as st
6
+ import pandas as pd
7
+ import torch
8
+ import pickle
9
+ import matplotlib.pyplot as plt
10
+ import seaborn as sns
11
+ from rdkit import Chem
12
+ from rdkit.Chem import Descriptors
13
+
14
+ # --------------------------------------------------
15
+ # Page config
16
+ # --------------------------------------------------
17
+ st.set_page_config(
18
+ page_title="ICH-NOVA | Future-Grade Drug Discovery",
19
+ layout="wide",
20
+ initial_sidebar_state="expanded"
21
+ )
22
+
23
+ # --------------------------------------------------
24
+ # Dark futuristic theme
25
+ # --------------------------------------------------
26
+ st.markdown("""
27
+ <style>
28
+ body { background-color: #0e0e0e; color: #d0d0d0; }
29
+ h1, h2, h3 { color: #f0f0f0; }
30
+ .stButton>button, .stDownloadButton>button {
31
+ background-color: #1a1a1a;
32
+ color: #d0d0d0;
33
+ border: 1px solid #555;
34
+ }
35
+ </style>
36
+ """, unsafe_allow_html=True)
37
+
38
+ st.title("🧬 ICH-NOVA")
39
+ st.subheader("Regulatory-aware, self-evolving AI drug discovery")
40
+ # --------------------------------------------------
41
+ # Safe loaders
42
+ # --------------------------------------------------
43
+ @st.cache_data
44
+ def load_embeddings():
45
+ try:
46
+ with open("data/processed/embeddings.pkl", "rb") as f:
47
+ return pickle.load(f)
48
+ except Exception:
49
+ return {}
50
+
51
+ @st.cache_data
52
+ def load_results():
53
+ try:
54
+ return pd.read_csv("final_candidates_rl.csv")
55
+ except Exception:
56
+ return pd.DataFrame()
57
+
58
+ @st.cache_resource
59
+ def load_stability_model():
60
+ from src.stability.stability_zone_iv import StabilityPredictor
61
+ model = StabilityPredictor(input_dim=256, hidden_dim=128)
62
+ model.load_state_dict(torch.load("models/stability_model.pt", map_location="cpu"))
63
+ model.eval()
64
+ return model
65
+
66
+ embeddings_dict = load_embeddings()
67
+ final_df = load_results()
68
+ stability_model = load_stability_model()
69
+ # --------------------------------------------------
70
+ # TOP: Molecule of Interest (Primary Input)
71
+ # --------------------------------------------------
72
+ st.markdown("### 🧪 Molecule of Interest — Instant Zone-IV Evaluation")
73
+
74
+ top_col1, top_col2 = st.columns([3, 1])
75
+
76
+ with top_col1:
77
+ top_smiles = st.text_input(
78
+ "Enter SMILES string",
79
+ placeholder="e.g. CC(=O)OC1=CC=CC=C1C(=O)O"
80
+ )
81
+
82
+ with top_col2:
83
+ top_predict = st.button("Predict Stability")
84
+
85
+ if top_predict and top_smiles.strip():
86
+ mol = Chem.MolFromSmiles(top_smiles)
87
+ if mol:
88
+ mw = Descriptors.MolWt(mol)
89
+ emb = embeddings_dict.get(top_smiles, torch.randn(256)).unsqueeze(0)
90
+ with torch.no_grad():
91
+ stab = stability_model(emb).item()
92
+
93
+ res1, res2 = st.columns(2)
94
+ res1.metric("Zone-IV Shelf-Life (days)", f"{stab:.2f}")
95
+ res2.metric("Molecular Weight", f"{mw:.2f}")
96
+
97
+ st.success("Prediction completed successfully.")
98
+ else:
99
+ st.error("Invalid SMILES string. Please check your input.")
100
+
101
+ st.markdown("---")
102
+
103
+
104
+
105
+ # --------------------------------------------------
106
+ # Normalize dataframe (CRITICAL FIX)
107
+ # --------------------------------------------------
108
+ required_cols = [
109
+ "iteration", "smiles", "binding_score",
110
+ "synthesis_score", "stability_pred",
111
+ "reward", "admet_pass"
112
+ ]
113
+
114
+ for col in required_cols:
115
+ if col not in final_df.columns:
116
+ final_df[col] = None
117
+
118
+ # If admet_pass missing → infer safely
119
+ final_df["admet_pass"] = final_df["admet_pass"].fillna(
120
+ final_df["stability_pred"].notna()
121
+ )
122
+
123
+ # --------------------------------------------------
124
+ # ADMET summary
125
+ # --------------------------------------------------
126
+ st.subheader("🧪 ADMET Filtering Summary")
127
+
128
+ total = len(final_df)
129
+ passed = int(final_df["admet_pass"].sum())
130
+ failed = total - passed
131
+
132
+ c1, c2, c3 = st.columns(3)
133
+ c1.metric("Total Molecules", total)
134
+ c2.metric("Passed ADMET", passed)
135
+ c3.metric("Rejected", failed)
136
+
137
+ # --------------------------------------------------
138
+ # Sidebar: Molecule explorer
139
+ # --------------------------------------------------
140
+ st.sidebar.header("🧪 Molecule Explorer")
141
+ user_smiles = st.sidebar.text_input("Enter SMILES")
142
+
143
+ if st.sidebar.button("Predict"):
144
+ if user_smiles.strip():
145
+ mol = Chem.MolFromSmiles(user_smiles)
146
+ if mol:
147
+ mw = Descriptors.MolWt(mol)
148
+ emb = embeddings_dict.get(user_smiles, torch.randn(256)).unsqueeze(0)
149
+ with torch.no_grad():
150
+ stab = stability_model(emb).item()
151
+ st.sidebar.success(f"Zone-IV Shelf-life: {stab:.2f} days")
152
+ st.sidebar.info(f"Molecular Weight: {mw:.2f}")
153
+ else:
154
+ st.sidebar.error("Invalid SMILES")
155
+
156
+ # --------------------------------------------------
157
+ # Molecule table
158
+ # --------------------------------------------------
159
+ st.header("Generated Molecules")
160
+ st.dataframe(
161
+ final_df.sort_values("admet_pass", ascending=False),
162
+ use_container_width=True
163
+ )
164
+
165
+ st.download_button(
166
+ "📥 Download CSV",
167
+ final_df.to_csv(index=False),
168
+ "ICH_NOVA_results.csv",
169
+ "text/csv"
170
+ )
171
+
172
+ # --------------------------------------------------
173
+ # Visualizations (SAFE)
174
+ # --------------------------------------------------
175
+ st.header("Visual Analysis")
176
+
177
+ numeric_df = final_df.dropna(subset=["reward", "binding_score", "stability_pred"])
178
+
179
+ # Reward vs Iteration
180
+ if not numeric_df.empty:
181
+ st.subheader("Average RL Reward per Iteration")
182
+ fig, ax = plt.subplots()
183
+ numeric_df.groupby("iteration")["reward"].mean().plot(ax=ax)
184
+ ax.set_xlabel("Iteration")
185
+ ax.set_ylabel("Reward")
186
+ st.pyplot(fig)
187
+
188
+ st.subheader("Binding vs Stability")
189
+ fig, ax = plt.subplots()
190
+ sns.scatterplot(
191
+ data=numeric_df,
192
+ x="stability_pred",
193
+ y="binding_score",
194
+ hue="iteration",
195
+ ax=ax
196
+ )
197
+ st.pyplot(fig)
198
+
199
+ st.subheader("Reward vs Stability")
200
+ fig, ax = plt.subplots()
201
+ sc = ax.scatter(
202
+ numeric_df["stability_pred"],
203
+ numeric_df["binding_score"],
204
+ c=numeric_df["reward"],
205
+ cmap="viridis"
206
+ )
207
+ plt.colorbar(sc, ax=ax)
208
+ st.pyplot(fig)
209
+ else:
210
+ st.warning("Not enough numeric data for plots yet.")
211
+
212
+ # --------------------------------------------------
213
+ # Ablation (optional & safe)
214
+ # --------------------------------------------------
215
+ st.header("Module Ablation Analysis")
216
+
217
+ try:
218
+ abl = pd.read_csv("ablation_results.csv")
219
+ num = abl.select_dtypes(include="number")
220
+ if not num.empty:
221
+ plot_df = num.melt(var_name="Module", value_name="Reward")
222
+ fig, ax = plt.subplots()
223
+ sns.boxplot(data=plot_df, x="Module", y="Reward", ax=ax)
224
+ st.pyplot(fig)
225
+ else:
226
+ st.info("Ablation file contains no numeric data.")
227
+ except Exception:
228
+ st.info("Ablation results not available.")
229
+
230
+ # --------------------------------------------------
231
+ # Footer
232
+ # --------------------------------------------------
233
+ st.markdown("---")
234
+ st.markdown(
235
+ "<center style='color:#888'>ICH-NOVA | Self-evolving AI for real-world drug discovery</center>",
236
+ unsafe_allow_html=True
237
+ )