andreamena commited on
Commit
5658bc5
Β·
verified Β·
1 Parent(s): 4a82a63

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +95 -8
app.py CHANGED
@@ -238,7 +238,7 @@ def generate_and_screen(num_to_gen, app_mode, toggle_view, motifs=[], limit_aro=
238
  "SA Score": f"{get_status_icon(sa, T_SA, False)} {sa:.2f}",
239
  "Confidence": f"{conf_pct:.0f}%",
240
  "Classification": status,
241
- "_s": s, "_t": t, "_status": "Excellent" if "Excellent" in status else "Review" if "Review" in status else "Bad"
242
  })
243
 
244
  if len(candidates) == 0:
@@ -270,7 +270,7 @@ def generate_and_screen(num_to_gen, app_mode, toggle_view, motifs=[], limit_aro=
270
 
271
  output_path = tempfile.NamedTemporaryFile(delete=False, suffix=".csv").name
272
  df.to_csv(output_path, index=False)
273
- return df.drop(columns=["_s", "_t", "_status"]), output_path, fig
274
 
275
  def calculate_weighted_rank(df, app_mode, w_s, w_t, w_d, w_sa): # Added 4 weight arguments
276
  if df is None or df.empty: return None, None
@@ -465,7 +465,7 @@ def check_constraints(mol, motifs, limit_aromatic, max_mw):
465
  "Cyclic-Aliphatic": "C1CCCCC1"
466
  }
467
 
468
- for m in motifs:
469
  if m in pattern_map:
470
  p = Chem.MolFromSmarts(pattern_map[m])
471
  if not mol.HasSubstructMatch(p):
@@ -480,6 +480,49 @@ def check_constraints(mol, motifs, limit_aromatic, max_mw):
480
 
481
  return True
482
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
483
  def generate_exports(df):
484
  if df is None or df.empty:
485
  return [None] * 4
@@ -530,6 +573,17 @@ def generate_exports(df):
530
 
531
  pdf.output(pdf_path)
532
  return csv_path, json_path, smiles_path, pdf_path
 
 
 
 
 
 
 
 
 
 
 
533
 
534
  # --- 6. GRADIO INTERFACE ---
535
  with gr.Blocks(title="PolymerDesignAI") as demo:
@@ -613,7 +667,18 @@ with gr.Blocks(title="PolymerDesignAI") as demo:
613
  label="Sort Candidates By",
614
  interactive=True
615
  )
616
-
 
 
 
 
 
 
 
 
 
 
 
617
  results_df = gr.DataFrame(label="Candidate Table (Click headers to sort)")
618
  full_results_state = gr.State()
619
 
@@ -735,8 +800,20 @@ with gr.Blocks(title="PolymerDesignAI") as demo:
735
  def clean_and_explain(choice, prop):
736
  smiles = choice.split(" ")[1] if choice and " " in choice else choice
737
  return explain_polymer(smiles, prop)
 
 
 
 
 
 
 
 
 
738
 
739
- run.click(fn=on_gen_complete, inputs=[num, app_mode, toggle_view, must_have, exclude_aromatic, max_mol_wt], outputs=[results_df, pareto_plot, selection_dropdown, full_results_state, explain_img, explain_bar])
 
 
 
740
 
741
  filter_btn_all.click(lambda x: filter_table(x, "All"), full_results_state, results_df)
742
  filter_btn_exc.click(lambda x: filter_table(x, "Excellent"), full_results_state, results_df)
@@ -764,10 +841,20 @@ with gr.Blocks(title="PolymerDesignAI") as demo:
764
  sort_dropdown.change(sort_table, [results_df, sort_dropdown], results_df)
765
  rank_btn.click(calculate_weighted_rank, [results_df, app_mode, w_strength, w_tg, w_degrad, w_sa], [results_df, download_csv])
766
  refine_btn.click(
767
- on_refine_start,
768
- [selection_dropdown, num, app_mode, toggle_view, must_have, exclude_aromatic, max_mol_wt],
769
- [results_df, pareto_plot, selection_dropdown, full_results_state, explain_img, explain_bar]
770
  )
 
 
 
 
 
 
 
 
 
 
771
  # Automatically update all download links whenever the results table changes
772
  results_df.change(
773
  fn=update_exports,
 
238
  "SA Score": f"{get_status_icon(sa, T_SA, False)} {sa:.2f}",
239
  "Confidence": f"{conf_pct:.0f}%",
240
  "Classification": status,
241
+ "_s": s, "_t": t, "Degradability_raw": d, "_status": "Excellent" if "Excellent" in status else "Review" if "Review" in status else "Bad"
242
  })
243
 
244
  if len(candidates) == 0:
 
270
 
271
  output_path = tempfile.NamedTemporaryFile(delete=False, suffix=".csv").name
272
  df.to_csv(output_path, index=False)
273
+ return df, output_path, fig
274
 
275
  def calculate_weighted_rank(df, app_mode, w_s, w_t, w_d, w_sa): # Added 4 weight arguments
276
  if df is None or df.empty: return None, None
 
465
  "Cyclic-Aliphatic": "C1CCCCC1"
466
  }
467
 
468
+ for m in active_motifs:
469
  if m in pattern_map:
470
  p = Chem.MolFromSmarts(pattern_map[m])
471
  if not mol.HasSubstructMatch(p):
 
480
 
481
  return True
482
 
483
+ def run_sensitivity_analysis(raw_df, t_s, t_t, t_d):
484
+ if raw_df is None or raw_df.empty:
485
+ return None, "No data to analyze", []
486
+
487
+ processed_df = raw_df.copy()
488
+
489
+ # Re-classification logic based on NEW thresholds
490
+ def reclassify(row):
491
+ s, t, d = row["_s"], row["_t"], row["Degradability_raw"]
492
+ failed = []
493
+ if s < t_s: failed.append("Strength")
494
+ if t < t_t: failed.append("Tg")
495
+ if d < t_d: failed.append("Degradability")
496
+
497
+ if not failed: return "Excellent βœ…"
498
+ if len(failed) <= 2: return f"Review (Failed {', '.join(failed)}) ⚠️"
499
+ return "Bad ❌"
500
+
501
+ processed_df["Classification"] = processed_df.apply(reclassify, axis=1)
502
+
503
+ # Calculate Market Readiness
504
+ total = len(processed_df)
505
+ passed = len(processed_df[processed_df["Classification"] == "Excellent βœ…"])
506
+ pass_pct = (passed / total) * 100 if total > 0 else 0
507
+
508
+ # Update icons in the visible columns for the user
509
+ def update_icons(row):
510
+ row["Strength (GPa)"] = f"{'🟒' if row['_s'] >= t_s else 'πŸ”΄'} {row['_s']:.3f}"
511
+ row["Tg (Β°C)"] = f"{'🟒' if row['_t'] >= t_t else 'πŸ”΄'} {row['_t']:.1f}"
512
+ row["Degradability"] = f"{'🟒' if row['Degradability_raw'] >= t_d else 'πŸ”΄'} {row['Degradability_raw']:.2f}"
513
+ return row
514
+
515
+ display_df = processed_df.apply(update_icons, axis=1)
516
+
517
+ status_msg = f"**{pass_pct:.1f}%** of candidates ({passed}/{total}) meet these industrial benchmarks."
518
+ # Color-coded badge for Gradio AnnotatedText
519
+ badge_color = "green" if pass_pct > 25 else "orange" if pass_pct > 5 else "red"
520
+ annotated_data = [(f"{pass_pct:.0f}% Pass Rate", badge_color)]
521
+
522
+ # Drop hidden columns before returning to the UI
523
+ final_cols = [c for c in display_df.columns if not c.startswith('_') and c != "Degradability_raw"]
524
+ return display_df[final_cols], status_msg, annotated_data
525
+
526
  def generate_exports(df):
527
  if df is None or df.empty:
528
  return [None] * 4
 
573
 
574
  pdf.output(pdf_path)
575
  return csv_path, json_path, smiles_path, pdf_path
576
+
577
+ def on_refine_wrapper(seed_with_icon, num, mode, view, motifs, limit_aro, max_mw):
578
+ # 1. Call existing refine logic (returns 6 items)
579
+ df, fig, dropdown, state, img, bar = on_refine_start(seed_with_icon, num, mode, view, motifs, limit_aro, max_mw)
580
+
581
+ # 2. Prepare the 2 extra items
582
+ init_msg = "Move sliders above to re-analyze refined batch."
583
+ init_anno = [("0% Pass Rate", "Awaiting Analysis")]
584
+
585
+ # 3. Return all 8 items
586
+ return df, fig, dropdown, state, img, bar, init_msg, init_anno
587
 
588
  # --- 6. GRADIO INTERFACE ---
589
  with gr.Blocks(title="PolymerDesignAI") as demo:
 
667
  label="Sort Candidates By",
668
  interactive=True
669
  )
670
+ # --- NEW: BENCHMARK SENSITIVITY ANALYSIS ---
671
+ with gr.Accordion("πŸ“Š Benchmark Sensitivity Analysis (Industrial What-if)", open=False):
672
+ gr.Markdown("Adjust thresholds to dynamically re-classify candidates and see market readiness.")
673
+ with gr.Row():
674
+ threshold_s = gr.Slider(0, 0.2, value=0.08, step=0.005, label="Min Strength (GPa)")
675
+ threshold_t = gr.Slider(0, 300, value=150, step=5, label="Min Tg (Β°C)")
676
+ threshold_d = gr.Slider(0, 1.0, value=0.10, step=0.05, label="Min Degradability")
677
+
678
+ with gr.Row():
679
+ pass_metric = gr.AnnotatedText(label="Market Readiness")
680
+ stats_box = gr.Markdown("### πŸ“ˆ Impact Summary")
681
+ # --------------------------------------------
682
  results_df = gr.DataFrame(label="Candidate Table (Click headers to sort)")
683
  full_results_state = gr.State()
684
 
 
800
  def clean_and_explain(choice, prop):
801
  smiles = choice.split(" ")[1] if choice and " " in choice else choice
802
  return explain_polymer(smiles, prop)
803
+
804
+ def on_gen_complete_wrapper(num, mode, view, motifs, limit_aro, max_mw, progress=gr.Progress()):
805
+ # 1. This function MUST return 6 items based on your code:
806
+ # (df, fig, dropdown, state_df, img, bar)
807
+ df, fig, dropdown, state, img, bar = on_gen_complete(num, mode, view, motifs, limit_aro, max_mw, progress)
808
+
809
+ # 2. Add the 2 sensitivity items
810
+ init_msg = "### πŸ“ˆ Impact Summary\nMove sliders above to see analysis."
811
+ init_anno = [("0% Pass Rate", "Awaiting Analysis")]
812
 
813
+ # 3. Return 8 items
814
+ return df, fig, dropdown, state, img, bar, init_msg, init_anno
815
+
816
+ run.click(fn=on_gen_complete_wrapper, inputs=[num, app_mode, toggle_view, must_have, exclude_aromatic, max_mol_wt], outputs=[results_df, pareto_plot, selection_dropdown, full_results_state, explain_img, explain_bar, stats_box, pass_metric])
817
 
818
  filter_btn_all.click(lambda x: filter_table(x, "All"), full_results_state, results_df)
819
  filter_btn_exc.click(lambda x: filter_table(x, "Excellent"), full_results_state, results_df)
 
841
  sort_dropdown.change(sort_table, [results_df, sort_dropdown], results_df)
842
  rank_btn.click(calculate_weighted_rank, [results_df, app_mode, w_strength, w_tg, w_degrad, w_sa], [results_df, download_csv])
843
  refine_btn.click(
844
+ fn=on_refine_wrapper, # Points to the new 8-output wrapper
845
+ inputs=[selection_dropdown, num, app_mode, toggle_view, must_have, exclude_aromatic, max_mol_wt],
846
+ outputs=[results_df, pareto_plot, selection_dropdown, full_results_state, explain_img, explain_bar, stats_box, pass_metric]
847
  )
848
+
849
+ # Sensitivity Triggers
850
+ slider_inputs = [full_results_state, threshold_s, threshold_t, threshold_d]
851
+ slider_outputs = [results_df, stats_box, pass_metric]
852
+
853
+ threshold_s.change(fn=run_sensitivity_analysis, inputs=slider_inputs, outputs=slider_outputs)
854
+ threshold_t.change(fn=run_sensitivity_analysis, inputs=slider_inputs, outputs=slider_outputs)
855
+ threshold_d.change(fn=run_sensitivity_analysis, inputs=slider_inputs, outputs=slider_outputs)
856
+
857
+
858
  # Automatically update all download links whenever the results table changes
859
  results_df.change(
860
  fn=update_exports,