m0ksh commited on
Commit
4da8903
·
verified ·
1 Parent(s): bd4c024

Sync from GitHub (preserve manual model files)

Browse files
.gitignore CHANGED
@@ -3,5 +3,8 @@ Data/**/*.tmp
3
  Data/**/*.log
4
  MLModels/**/*.pt
5
  MLModels/**/*.pth
6
- StreamlitApp/utils/__pycache__/
 
 
 
7
  StreamlitApp/models/*.pt
 
3
  Data/**/*.log
4
  MLModels/**/*.pt
5
  MLModels/**/*.pth
6
+ # Python bytecode (safe to ignore everywhere)
7
+ __pycache__/
8
+ *.py[cod]
9
+ *$py.class
10
  StreamlitApp/models/*.pt
StreamlitApp/StreamlitApp.py CHANGED
@@ -27,9 +27,9 @@ from utils.ui_helpers import (
27
  from utils.peptide_extras import (
28
  KNOWN_AMPS,
29
  MAX_3D_SEQUENCE_LENGTH,
30
- HELIX_WHEEL_LEGEND_MARKDOWN,
31
- STRUCTURE_3D_INTERPRETATION_MARKDOWN,
32
- STRUCTURE_3D_LEGEND_MARKDOWN,
33
  find_most_similar,
34
  build_importance_map_html,
35
  plot_helical_wheel,
@@ -405,7 +405,7 @@ elif page == "Analyze":
405
  "left:50%;"
406
  "top:125%;"
407
  "transform:translateX(-50%);"
408
- "max-width:320px;"
409
  "white-space:normal;"
410
  "padding:8px 10px;"
411
  "background:rgba(30,30,30,0.95);"
@@ -558,24 +558,6 @@ elif page == "Optimize":
558
  f"Hydrophobicity: **{summary['hydro_change']}** (orig {summary['hydro_orig']}, final {summary['hydro_final']})"
559
  )
560
 
561
- if improved_seq and str(improved_seq).strip():
562
- st.divider()
563
- st.subheader("Optimized Structure Visualization")
564
- st.caption(
565
- "Helix-like CA trace approximation for the optimized sequence (not an experimental fold)."
566
- )
567
- opt_clean = "".join(c for c in str(improved_seq).upper() if not c.isspace())
568
- if len(opt_clean) > MAX_3D_SEQUENCE_LENGTH:
569
- st.warning("Sequence too long for 3D visualization (max 60 residues).")
570
- elif render_3d_structure(improved_seq, enhanced=False):
571
- with st.expander("Visualization info", expanded=False):
572
- st.markdown(STRUCTURE_3D_LEGEND_MARKDOWN)
573
- st.markdown(STRUCTURE_3D_INTERPRETATION_MARKDOWN)
574
- else:
575
- st.info(
576
- "3D view unavailable (install **py3dmol** in your environment, or try again after redeploy)."
577
- )
578
-
579
  st.divider()
580
  # Mutation Heatmap
581
  st.subheader("Mutation Heatmap (Changed Residues Highlighted)")
@@ -607,6 +589,23 @@ elif page == "Optimize":
607
  # VISUALIZE PEPTIDE PAGE
608
  elif page == "Visualize Peptide":
609
  st.header("Visualize Peptide")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
610
  st.caption(
611
  "High-impact single-sequence view. **Blue / red / green / gray** match the 3D model, helical wheel, "
612
  "and functional residue map."
@@ -617,28 +616,20 @@ elif page == "Visualize Peptide":
617
  if sug:
618
  st.session_state.visualize_peptide_input = sug
619
 
620
- c_btn, c_auto = st.columns([2, 1])
621
- with c_btn:
622
- if st.button("Use suggested sequence (Predict / Analyze / Optimize)", key="btn_viz_peptide_prefill"):
623
- st.session_state.visualize_peptide_input = _prefill_peptide_viz_sequence()
624
- rerun_fn = getattr(st, "rerun", None) or getattr(st, "experimental_rerun", None)
625
- if rerun_fn:
626
- rerun_fn()
627
- with c_auto:
628
- st.checkbox("Auto-run when sequence changes", value=False, key="viz_peptide_auto_run")
629
 
630
  st.text_input(
631
  "Peptide sequence",
632
  key="visualize_peptide_input",
633
  placeholder="One-letter amino-acid sequence",
634
- help="Empty field picks up your last optimized, analyzed, or predicted sequence when you open this page.",
635
  )
636
 
637
  seq_viz = (st.session_state.get("visualize_peptide_input") or "").strip()
638
  clean_viz = "".join(c for c in seq_viz.upper() if not c.isspace())
639
  if not clean_viz:
640
  st.session_state.viz_peptide_last_computed = ""
641
- st.info("Enter a sequence above, or click **Use suggested sequence** to pull from other tools.")
642
  else:
643
  run_viz = st.button("Run visualization", type="primary", key="viz_peptide_run_btn")
644
  auto_on = bool(st.session_state.get("viz_peptide_auto_run"))
@@ -666,12 +657,11 @@ elif page == "Visualize Peptide":
666
  col_l, col_r = st.columns(2)
667
  with col_l:
668
  st.subheader("3D structural approximation (py3Dmol)")
669
- st.caption("Smoothed helical CA trace; colored spheres follow the same scheme as the wheel below.")
670
  if len(clean_viz) <= MAX_3D_SEQUENCE_LENGTH:
671
- if render_3d_structure(clean_viz, enhanced=True):
672
- with st.expander("Legend & interpretation (3D)", expanded=False):
673
- st.markdown(STRUCTURE_3D_LEGEND_MARKDOWN)
674
- st.markdown(STRUCTURE_3D_INTERPRETATION_MARKDOWN)
675
  else:
676
  st.info("3D view unavailable (install **py3dmol** in your environment).")
677
  else:
@@ -683,31 +673,32 @@ elif page == "Visualize Peptide":
683
  fig_wheel = plot_helical_wheel(clean_viz)
684
  st.pyplot(fig_wheel, use_container_width=True)
685
  plt.close(fig_wheel)
686
- with st.expander("Helical wheel legend", expanded=False):
687
- st.markdown(HELIX_WHEEL_LEGEND_MARKDOWN)
688
-
689
- st.subheader("Functional region map")
690
- st.caption("Residue-level chemistry; colors align with the 3D view and wheel.")
691
- st.markdown(build_importance_map_html(clean_viz), unsafe_allow_html=True)
692
- with st.expander("Color key (sequence map)", expanded=False):
693
- st.markdown(STRUCTURE_3D_LEGEND_MARKDOWN)
694
-
695
- st.subheader("Most similar known AMP")
696
- st.caption(
697
- f"Compared to **{len(KNOWN_AMPS)}** unique AMP sequences (label = 1 in `Data/ampData.csv`)."
698
- )
699
- match_seq, sim_score = find_most_similar(clean_viz)
700
- if match_seq is not None:
701
- st.write(f"**Best match:** `{match_seq}`")
702
- st.write(f"**Similarity score:** **{sim_score:.3f}** (position match / max length)")
703
- if sim_score > 0.6:
704
- st.success("High similarity to a known AMP in the reference set.")
705
- elif sim_score > 0.3:
706
- st.warning("Moderate similarity interpret with care.")
707
- else:
708
- st.error("Low similarity — sequence is distant from reference AMPs.")
709
  else:
710
- st.warning("Could not compute similarity (empty sequence after cleaning).")
 
 
711
 
712
  elif clean_viz != (st.session_state.get("viz_peptide_last_computed") or ""):
713
  st.caption("Click **Run visualization** or enable **Auto-run** to update the figures.")
@@ -816,7 +807,7 @@ PeptideAI is a lightweight Streamlit app for exploring antimicrobial peptide (AM
816
  It uses a trained neural network to estimate whether a peptide is likely to be antimicrobial, then helps you interpret and improve candidates:
817
  - **Predict**: batch predictions from multi-line or FASTA input, length warnings, persisted results, top-candidate highlight, and CSV export.
818
  - **Analyze**: single-sequence numerical & textual analysis — AMP prediction, composition, physicochemical table + radar, and exportable report (no 3D on this page).
819
- - **Optimize**: guided sequence optimization with mutation heatmap, step table, confidence vs. step plot, and an optional 3D view of the optimized sequence.
820
  - **Visualize Peptide**: one sequence with consistent coloring across functional map, helical wheel, and optional 3D approximation, plus similarity to known AMPs.
821
  - **Visualize t-SNE**: upload many sequences, embed with the model, run t-SNE, and explore clusters with filters and hover metadata.
822
  - **About**: this overview and disclaimer.
 
27
  from utils.peptide_extras import (
28
  KNOWN_AMPS,
29
  MAX_3D_SEQUENCE_LENGTH,
30
+ COMPACT_3D_LEGEND,
31
+ COMPACT_MAP_LEGEND,
32
+ COMPACT_WHEEL_LEGEND,
33
  find_most_similar,
34
  build_importance_map_html,
35
  plot_helical_wheel,
 
405
  "left:50%;"
406
  "top:125%;"
407
  "transform:translateX(-50%);"
408
+ "max-width:560px;"
409
  "white-space:normal;"
410
  "padding:8px 10px;"
411
  "background:rgba(30,30,30,0.95);"
 
558
  f"Hydrophobicity: **{summary['hydro_change']}** (orig {summary['hydro_orig']}, final {summary['hydro_final']})"
559
  )
560
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
561
  st.divider()
562
  # Mutation Heatmap
563
  st.subheader("Mutation Heatmap (Changed Residues Highlighted)")
 
589
  # VISUALIZE PEPTIDE PAGE
590
  elif page == "Visualize Peptide":
591
  st.header("Visualize Peptide")
592
+ # Tighter legend expanders (summary row + scrollable body)
593
+ st.markdown(
594
+ """
595
+ <style>
596
+ div[data-testid="stExpander"] details > summary {
597
+ padding-top: 0.3rem !important;
598
+ padding-bottom: 0.3rem !important;
599
+ min-height: 2rem !important;
600
+ }
601
+ div[data-testid="stExpander"] details div[data-testid="stMarkdownContainer"] {
602
+ max-height: 6.5rem;
603
+ overflow-y: auto;
604
+ }
605
+ </style>
606
+ """,
607
+ unsafe_allow_html=True,
608
+ )
609
  st.caption(
610
  "High-impact single-sequence view. **Blue / red / green / gray** match the 3D model, helical wheel, "
611
  "and functional residue map."
 
616
  if sug:
617
  st.session_state.visualize_peptide_input = sug
618
 
619
+ st.checkbox("Auto-run when sequence changes", value=False, key="viz_peptide_auto_run")
 
 
 
 
 
 
 
 
620
 
621
  st.text_input(
622
  "Peptide sequence",
623
  key="visualize_peptide_input",
624
  placeholder="One-letter amino-acid sequence",
625
+ help="When empty, your last optimized / analyzed / predicted sequence is filled in automatically.",
626
  )
627
 
628
  seq_viz = (st.session_state.get("visualize_peptide_input") or "").strip()
629
  clean_viz = "".join(c for c in seq_viz.upper() if not c.isspace())
630
  if not clean_viz:
631
  st.session_state.viz_peptide_last_computed = ""
632
+ st.info("Enter a sequence above (or navigate from Predict / Analyze / Optimize to auto-fill when empty).")
633
  else:
634
  run_viz = st.button("Run visualization", type="primary", key="viz_peptide_run_btn")
635
  auto_on = bool(st.session_state.get("viz_peptide_auto_run"))
 
657
  col_l, col_r = st.columns(2)
658
  with col_l:
659
  st.subheader("3D structural approximation (py3Dmol)")
660
+ st.caption("Smoothed helical CA trace; colored spheres follow the same scheme as the wheel.")
661
  if len(clean_viz) <= MAX_3D_SEQUENCE_LENGTH:
662
+ if render_3d_structure(clean_viz, enhanced=True, spin=False):
663
+ with st.expander("3D · legend", expanded=False):
664
+ st.markdown(COMPACT_3D_LEGEND)
 
665
  else:
666
  st.info("3D view unavailable (install **py3dmol** in your environment).")
667
  else:
 
673
  fig_wheel = plot_helical_wheel(clean_viz)
674
  st.pyplot(fig_wheel, use_container_width=True)
675
  plt.close(fig_wheel)
676
+ with st.expander("Wheel · legend", expanded=False):
677
+ st.markdown(COMPACT_WHEEL_LEGEND)
678
+
679
+ st.divider()
680
+ st.subheader("Functional region map")
681
+ st.caption("Residue-level chemistry; colors align with the 3D view and wheel.")
682
+ st.markdown(build_importance_map_html(clean_viz), unsafe_allow_html=True)
683
+ with st.expander("Map · legend", expanded=False):
684
+ st.markdown(COMPACT_MAP_LEGEND)
685
+
686
+ st.subheader("Most similar known AMP")
687
+ st.caption(
688
+ f"Compared to **{len(KNOWN_AMPS)}** unique AMP sequences (label = 1 in `Data/ampData.csv`)."
689
+ )
690
+ match_seq, sim_score = find_most_similar(clean_viz)
691
+ if match_seq is not None:
692
+ st.write(f"**Best match:** `{match_seq}`")
693
+ st.write(f"**Similarity score:** **{sim_score:.3f}** (position match / max length)")
694
+ if sim_score > 0.6:
695
+ st.success("High similarity to a known AMP in the reference set.")
696
+ elif sim_score > 0.3:
697
+ st.warning("Moderate similarity — interpret with care.")
 
698
  else:
699
+ st.error("Low similarity sequence is distant from reference AMPs.")
700
+ else:
701
+ st.warning("Could not compute similarity (empty sequence after cleaning).")
702
 
703
  elif clean_viz != (st.session_state.get("viz_peptide_last_computed") or ""):
704
  st.caption("Click **Run visualization** or enable **Auto-run** to update the figures.")
 
807
  It uses a trained neural network to estimate whether a peptide is likely to be antimicrobial, then helps you interpret and improve candidates:
808
  - **Predict**: batch predictions from multi-line or FASTA input, length warnings, persisted results, top-candidate highlight, and CSV export.
809
  - **Analyze**: single-sequence numerical & textual analysis — AMP prediction, composition, physicochemical table + radar, and exportable report (no 3D on this page).
810
+ - **Optimize**: guided sequence optimization with mutation heatmap, step table, and confidence vs. step plot.
811
  - **Visualize Peptide**: one sequence with consistent coloring across functional map, helical wheel, and optional 3D approximation, plus similarity to known AMPs.
812
  - **Visualize t-SNE**: upload many sequences, embed with the model, run t-SNE, and explore clusters with filters and hover metadata.
813
  - **About**: this overview and disclaimer.
StreamlitApp/utils/peptide_extras.py CHANGED
@@ -172,38 +172,58 @@ HELIX_WHEEL_LEGEND_MARKDOWN: str = """
172
  Residues are placed using a **100° step** per position (common α-helical wheel convention). This is a **2D projection**, not a solved 3D structure.
173
  """
174
 
 
 
 
 
 
 
 
 
 
 
 
 
175
 
176
  def plot_helical_wheel(sequence: str, figsize: Tuple[float, float] = (6.2, 6.2)) -> Any:
177
  """
178
  Amphipathic-style helical wheel (matplotlib polar). Colors match 3D / HTML maps.
 
179
  """
180
  import matplotlib.pyplot as plt
 
181
 
182
  clean = "".join(c for c in (sequence or "").upper() if not c.isspace())
183
  n = len(clean)
184
  fig, ax = plt.subplots(figsize=figsize, subplot_kw={"projection": "polar"})
 
185
  if n == 0:
 
186
  ax.set_title("Helical wheel (empty sequence)", pad=12)
187
  return fig
188
 
 
 
189
  angles_deg = np.array([i * 100.0 for i in range(n)], dtype=float) % 360.0
190
  angles_rad = np.deg2rad(angles_deg)
191
  width = np.deg2rad(min(360.0 / max(n, 1) * 0.92, 0.45))
192
  r0, r1 = 0.35, 1.0
 
193
  for i, aa in enumerate(clean):
194
  th = angles_rad[i]
195
  color = residue_color_mpl(aa)
196
- ax.bar(th, r1 - r0, width=width, bottom=r0, color=color, edgecolor="black", linewidth=0.35, align="center")
197
- ax.text(
198
  th,
199
  (r0 + r1) / 2.0,
200
  aa,
201
  ha="center",
202
  va="center",
203
- fontsize=max(6, min(11, int(220 / max(n, 1)))),
204
- color="white",
205
  fontweight="bold",
206
  )
 
207
 
208
  ax.set_theta_zero_location("N")
209
  ax.set_theta_direction(-1)
@@ -211,8 +231,7 @@ def plot_helical_wheel(sequence: str, figsize: Tuple[float, float] = (6.2, 6.2))
211
  ax.set_yticklabels([])
212
  ax.set_xticklabels([])
213
  ax.grid(False)
214
- ax.set_title("Helical wheel (α-helix projection, approximate)", pad=14, fontsize=11)
215
- fig.patch.set_facecolor("white")
216
  return fig
217
 
218
 
@@ -299,10 +318,12 @@ def render_3d_structure(
299
  iframe_height: int = 420,
300
  *,
301
  enhanced: bool = False,
 
302
  ) -> bool:
303
  """
304
  Render py3Dmol view: gray stick backbone + colored spheres per residue (CA-only PDB).
305
- When enhanced=True: smoother helix path, slightly larger spheres, optional spin, more labels.
 
306
  Not a real folded structure — helix-like CA trace only.
307
  """
308
  import streamlit.components.v1 as components
@@ -373,7 +394,7 @@ def render_3d_structure(
373
 
374
  view.zoomTo()
375
 
376
- if enhanced:
377
  try:
378
  view.spin(True)
379
  except Exception:
 
172
  Residues are placed using a **100° step** per position (common α-helical wheel convention). This is a **2D projection**, not a solved 3D structure.
173
  """
174
 
175
+ # Short blurbs for compact UI expanders (Visualize Peptide page)
176
+ COMPACT_3D_LEGEND: str = (
177
+ "**Blue** K,R,H · **Red** D,E · **Green** hydrophobic (AVLIMFWY) · **Gray** other. "
178
+ "Helix trace is an approximation, not an experimental structure."
179
+ )
180
+ COMPACT_WHEEL_LEGEND: str = (
181
+ "**100°** per residue. Cationic (blue) and hydrophobic (green) faces often matter for membrane-active helices."
182
+ )
183
+ COMPACT_MAP_LEGEND: str = (
184
+ "Same scheme as 3D and wheel: charged vs hydrophobic distribution along the sequence."
185
+ )
186
+
187
 
188
  def plot_helical_wheel(sequence: str, figsize: Tuple[float, float] = (6.2, 6.2)) -> Any:
189
  """
190
  Amphipathic-style helical wheel (matplotlib polar). Colors match 3D / HTML maps.
191
+ Uses dark text for contrast on all wedge colors (avoids white-on-light issues in Streamlit).
192
  """
193
  import matplotlib.pyplot as plt
194
+ from matplotlib import patheffects as pe
195
 
196
  clean = "".join(c for c in (sequence or "").upper() if not c.isspace())
197
  n = len(clean)
198
  fig, ax = plt.subplots(figsize=figsize, subplot_kw={"projection": "polar"})
199
+ fig.patch.set_facecolor("white")
200
  if n == 0:
201
+ ax.set_facecolor("#f5f5f5")
202
  ax.set_title("Helical wheel (empty sequence)", pad=12)
203
  return fig
204
 
205
+ ax.set_facecolor("#f5f5f5")
206
+
207
  angles_deg = np.array([i * 100.0 for i in range(n)], dtype=float) % 360.0
208
  angles_rad = np.deg2rad(angles_deg)
209
  width = np.deg2rad(min(360.0 / max(n, 1) * 0.92, 0.45))
210
  r0, r1 = 0.35, 1.0
211
+ fs = max(7, min(12, int(240 / max(n, 1))))
212
  for i, aa in enumerate(clean):
213
  th = angles_rad[i]
214
  color = residue_color_mpl(aa)
215
+ ax.bar(th, r1 - r0, width=width, bottom=r0, color=color, edgecolor="#222222", linewidth=0.5, align="center")
216
+ t = ax.text(
217
  th,
218
  (r0 + r1) / 2.0,
219
  aa,
220
  ha="center",
221
  va="center",
222
+ fontsize=fs,
223
+ color="#111111",
224
  fontweight="bold",
225
  )
226
+ t.set_path_effects([pe.withStroke(linewidth=2.5, foreground="white")])
227
 
228
  ax.set_theta_zero_location("N")
229
  ax.set_theta_direction(-1)
 
231
  ax.set_yticklabels([])
232
  ax.set_xticklabels([])
233
  ax.grid(False)
234
+ ax.set_title("Helical wheel (α-helix projection, approximate)", pad=14, fontsize=11, color="#111111")
 
235
  return fig
236
 
237
 
 
318
  iframe_height: int = 420,
319
  *,
320
  enhanced: bool = False,
321
+ spin: bool = False,
322
  ) -> bool:
323
  """
324
  Render py3Dmol view: gray stick backbone + colored spheres per residue (CA-only PDB).
325
+ When enhanced=True: smoother helix path, slightly larger spheres, more labels.
326
+ When spin=True: enable viewer spin (off by default).
327
  Not a real folded structure — helix-like CA trace only.
328
  """
329
  import streamlit.components.v1 as components
 
394
 
395
  view.zoomTo()
396
 
397
+ if spin:
398
  try:
399
  view.spin(True)
400
  except Exception: