maskil commited on
Commit
bdf2f5c
Β·
verified Β·
1 Parent(s): 35b58a4

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +80 -16
app.py CHANGED
@@ -321,6 +321,16 @@ def find_flip_point(data, fit_params):
321
  return None
322
 
323
 
 
 
 
 
 
 
 
 
 
 
324
  # ═════════════════════════════════════════════════════════════════════════
325
  # Consensus, Routing & State Machine
326
  # ═════════════════════════════════════════════════════════════════════════
@@ -497,6 +507,8 @@ def ingest_files(files):
497
  # 22: exp_data
498
  # 23-28: flip_reg_info, flip_reg_tag, flip_ai_info, flip_ai_tag,
499
  # flip_dual_col, flip_dual_state (dual/diverging mode)
 
 
500
 
501
  def _empty_ws(msg=""):
502
  return (msg,
@@ -508,7 +520,8 @@ def _empty_ws(msg=""):
508
  "", None, gr.Column(visible=False), None,
509
  None,
510
  "", None, "", None, gr.Column(visible=False), None,
511
- gr.Column(visible=True), gr.Column(visible=True))
 
512
 
513
 
514
  def fetch_next(username):
@@ -607,7 +620,8 @@ def fetch_next(username):
607
  flip_reg_info, None, flip_ai_info, None,
608
  gr.Column(visible=show_flip_dual), dual_state,
609
  gr.Column(visible=show_flip_reg_sub),
610
- gr.Column(visible=show_flip_ai_sub))
 
611
  except Exception as e:
612
  print(f"fetch_next render error: {e}\n{traceback.format_exc()}")
613
  return _empty_ws(f"❌ Render failed: {e}")
@@ -738,7 +752,7 @@ def reset_ratings():
738
 
739
 
740
  def update_workspace_state(data_val, fit_s_val, fit_reg_val, fit_ai_val,
741
- model_preds, overlap, exp_data):
742
  """Centralized state recalculation. Called when user changes any rating.
743
  Recomputes flip points, redraws plot, recalculates all visibility.
744
 
@@ -748,18 +762,22 @@ def update_workspace_state(data_val, fit_s_val, fit_reg_val, fit_ai_val,
748
  - None (cleared) or Borderline/Perfect β†’ show sections
749
  Flip-point gating:
750
  - Uses effective quality (user > model) for computation
 
 
 
751
 
752
- Outputs 15 items: (plot, fs_col, fd_col,
753
  flip_info, flip_tag, flip_col, flip_state,
754
  flip_reg_info, flip_reg_tag, flip_ai_info, flip_ai_tag,
755
  flip_dual_col, flip_dual_state,
756
- flip_reg_sub, flip_ai_sub)
757
  """
758
  _empty = (None,
759
  gr.Column(visible=True), gr.Column(visible=False),
760
  "", None, gr.Column(visible=False), None,
761
  "", None, "", None, gr.Column(visible=False), None,
762
- gr.Column(visible=True), gr.Column(visible=True))
 
763
  if exp_data is None:
764
  return _empty
765
 
@@ -803,9 +821,13 @@ def update_workspace_state(data_val, fit_s_val, fit_reg_val, fit_ai_val,
803
  if not overlap and ai_params and ai_fit_gate:
804
  flip_ai = find_flip_point(raw_data, ai_params)
805
 
806
- # ── Redraw plot ──
807
- fig = create_plot(raw_data, fname, reg_params, ai_params, overlap,
808
- flip_reg=flip_reg, flip_ai=flip_ai)
 
 
 
 
809
 
810
  # ── Section visibility ──
811
  show_fit_single = overlap and ui_show
@@ -851,7 +873,8 @@ def update_workspace_state(data_val, fit_s_val, fit_reg_val, fit_ai_val,
851
  gr.Column(visible=show_flip_dual),
852
  dual_state,
853
  gr.Column(visible=show_flip_reg_sub),
854
- gr.Column(visible=show_flip_ai_sub))
 
855
 
856
 
857
  # ═════════════════════════════════════════════════════���═══════════════════
@@ -1199,6 +1222,7 @@ def build_ui():
1199
 
1200
  ws_exp_data = gr.State(None)
1201
  ws_flip_state = gr.State(None)
 
1202
 
1203
  ws_reset = gr.Button("πŸ”„ Clear All Ratings", size="sm")
1204
 
@@ -1270,29 +1294,29 @@ def build_ui():
1270
  ws_flip_tag, ws_flip_reg_tag, ws_flip_ai_tag]).then(
1271
  update_workspace_state,
1272
  [ws_dr, ws_fs, ws_fr, ws_fa,
1273
- ws_preds, ws_overlap, ws_exp_data],
1274
  [ws_plot, ws_fs_col, ws_fd_col,
1275
  ws_flip_info, ws_flip_tag, ws_flip_col, ws_flip_state,
1276
  ws_flip_reg_info, ws_flip_reg_tag,
1277
  ws_flip_ai_info, ws_flip_ai_tag,
1278
  ws_flip_dual_col, ws_flip_dual_state,
1279
- ws_flip_reg_sub, ws_flip_ai_sub])
1280
 
1281
  # Centralized state recalculation on any rating change
1282
  _state_inputs = [ws_dr, ws_fs, ws_fr, ws_fa,
1283
- ws_preds, ws_overlap, ws_exp_data]
1284
  _state_outputs = [ws_plot, ws_fs_col, ws_fd_col,
1285
  ws_flip_info, ws_flip_tag, ws_flip_col, ws_flip_state,
1286
  ws_flip_reg_info, ws_flip_reg_tag,
1287
  ws_flip_ai_info, ws_flip_ai_tag,
1288
  ws_flip_dual_col, ws_flip_dual_state,
1289
- ws_flip_reg_sub, ws_flip_ai_sub]
1290
  ws_dr.input(update_workspace_state, _state_inputs, _state_outputs)
1291
  ws_fs.input(update_workspace_state, _state_inputs, _state_outputs)
1292
  ws_fr.input(update_workspace_state, _state_inputs, _state_outputs)
1293
  ws_fa.input(update_workspace_state, _state_inputs, _state_outputs)
1294
 
1295
- # Output list β€” 31 components
1296
  ws_outs = [ws_status, ws_plot, ws_info, ws_ov_msg, ws_conf,
1297
  ws_fs_col, ws_fd_col,
1298
  ws_dr, ws_fs, ws_fr, ws_fa,
@@ -1303,7 +1327,8 @@ def build_ui():
1303
  ws_flip_reg_info, ws_flip_reg_tag,
1304
  ws_flip_ai_info, ws_flip_ai_tag,
1305
  ws_flip_dual_col, ws_flip_dual_state,
1306
- ws_flip_reg_sub, ws_flip_ai_sub]
 
1307
 
1308
  ws_fetch.click(fetch_next, [ws_user], ws_outs)
1309
  ws_submit.click(submit_feedback,
@@ -1495,6 +1520,45 @@ def run_validation_tests():
1495
  check("0-vote docs served last",
1496
  sorted_docs[2]['num_votes'] == 0 and sorted_docs[3]['num_votes'] == 0)
1497
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1498
  print(f"\n{'=' * 60}")
1499
  print(f"RESULTS: {passed} passed, {failed} failed")
1500
  print(f"{'=' * 60}")
 
321
  return None
322
 
323
 
324
+ def _plot_fingerprint(flip_reg, flip_ai, overlap):
325
+ """Create a hashable fingerprint of the plot's visual state.
326
+ The plot only changes when flip-point markers or overlap status change;
327
+ raw data and fit curves are constant for a given experiment."""
328
+ fr = (flip_reg['x'], flip_reg['y'], flip_reg['type']) if flip_reg else None
329
+ fa = (flip_ai['x'], flip_ai['y'], flip_ai['type']) if flip_ai else None
330
+ return (fr, fa, overlap)
331
+
332
+
333
+
334
  # ═════════════════════════════════════════════════════════════════════════
335
  # Consensus, Routing & State Machine
336
  # ═════════════════════════════════════════════════════════════════════════
 
507
  # 22: exp_data
508
  # 23-28: flip_reg_info, flip_reg_tag, flip_ai_info, flip_ai_tag,
509
  # flip_dual_col, flip_dual_state (dual/diverging mode)
510
+ # 29-30: flip_reg_sub, flip_ai_sub
511
+ # 31: plot_fp (plot fingerprint state for skip optimization)
512
 
513
  def _empty_ws(msg=""):
514
  return (msg,
 
520
  "", None, gr.Column(visible=False), None,
521
  None,
522
  "", None, "", None, gr.Column(visible=False), None,
523
+ gr.Column(visible=True), gr.Column(visible=True),
524
+ None)
525
 
526
 
527
  def fetch_next(username):
 
620
  flip_reg_info, None, flip_ai_info, None,
621
  gr.Column(visible=show_flip_dual), dual_state,
622
  gr.Column(visible=show_flip_reg_sub),
623
+ gr.Column(visible=show_flip_ai_sub),
624
+ None)
625
  except Exception as e:
626
  print(f"fetch_next render error: {e}\n{traceback.format_exc()}")
627
  return _empty_ws(f"❌ Render failed: {e}")
 
752
 
753
 
754
  def update_workspace_state(data_val, fit_s_val, fit_reg_val, fit_ai_val,
755
+ model_preds, overlap, exp_data, prev_fp):
756
  """Centralized state recalculation. Called when user changes any rating.
757
  Recomputes flip points, redraws plot, recalculates all visibility.
758
 
 
762
  - None (cleared) or Borderline/Perfect β†’ show sections
763
  Flip-point gating:
764
  - Uses effective quality (user > model) for computation
765
+ Plot optimization:
766
+ - Uses _plot_fingerprint to detect if the plot's visual state changed.
767
+ - Returns gr.skip() for the plot output when unchanged, saving bandwidth.
768
 
769
+ Outputs 16 items: (plot, fs_col, fd_col,
770
  flip_info, flip_tag, flip_col, flip_state,
771
  flip_reg_info, flip_reg_tag, flip_ai_info, flip_ai_tag,
772
  flip_dual_col, flip_dual_state,
773
+ flip_reg_sub, flip_ai_sub, new_fp)
774
  """
775
  _empty = (None,
776
  gr.Column(visible=True), gr.Column(visible=False),
777
  "", None, gr.Column(visible=False), None,
778
  "", None, "", None, gr.Column(visible=False), None,
779
+ gr.Column(visible=True), gr.Column(visible=True),
780
+ None)
781
  if exp_data is None:
782
  return _empty
783
 
 
821
  if not overlap and ai_params and ai_fit_gate:
822
  flip_ai = find_flip_point(raw_data, ai_params)
823
 
824
+ # ── Plot optimization: skip rebuild if visual state unchanged ──
825
+ new_fp = _plot_fingerprint(flip_reg, flip_ai, overlap)
826
+ if new_fp == prev_fp:
827
+ fig = gr.skip()
828
+ else:
829
+ fig = create_plot(raw_data, fname, reg_params, ai_params, overlap,
830
+ flip_reg=flip_reg, flip_ai=flip_ai)
831
 
832
  # ── Section visibility ──
833
  show_fit_single = overlap and ui_show
 
873
  gr.Column(visible=show_flip_dual),
874
  dual_state,
875
  gr.Column(visible=show_flip_reg_sub),
876
+ gr.Column(visible=show_flip_ai_sub),
877
+ new_fp)
878
 
879
 
880
  # ═════════════════════════════════════════════════════���═══════════════════
 
1222
 
1223
  ws_exp_data = gr.State(None)
1224
  ws_flip_state = gr.State(None)
1225
+ ws_plot_fp = gr.State(None)
1226
 
1227
  ws_reset = gr.Button("πŸ”„ Clear All Ratings", size="sm")
1228
 
 
1294
  ws_flip_tag, ws_flip_reg_tag, ws_flip_ai_tag]).then(
1295
  update_workspace_state,
1296
  [ws_dr, ws_fs, ws_fr, ws_fa,
1297
+ ws_preds, ws_overlap, ws_exp_data, ws_plot_fp],
1298
  [ws_plot, ws_fs_col, ws_fd_col,
1299
  ws_flip_info, ws_flip_tag, ws_flip_col, ws_flip_state,
1300
  ws_flip_reg_info, ws_flip_reg_tag,
1301
  ws_flip_ai_info, ws_flip_ai_tag,
1302
  ws_flip_dual_col, ws_flip_dual_state,
1303
+ ws_flip_reg_sub, ws_flip_ai_sub, ws_plot_fp])
1304
 
1305
  # Centralized state recalculation on any rating change
1306
  _state_inputs = [ws_dr, ws_fs, ws_fr, ws_fa,
1307
+ ws_preds, ws_overlap, ws_exp_data, ws_plot_fp]
1308
  _state_outputs = [ws_plot, ws_fs_col, ws_fd_col,
1309
  ws_flip_info, ws_flip_tag, ws_flip_col, ws_flip_state,
1310
  ws_flip_reg_info, ws_flip_reg_tag,
1311
  ws_flip_ai_info, ws_flip_ai_tag,
1312
  ws_flip_dual_col, ws_flip_dual_state,
1313
+ ws_flip_reg_sub, ws_flip_ai_sub, ws_plot_fp]
1314
  ws_dr.input(update_workspace_state, _state_inputs, _state_outputs)
1315
  ws_fs.input(update_workspace_state, _state_inputs, _state_outputs)
1316
  ws_fr.input(update_workspace_state, _state_inputs, _state_outputs)
1317
  ws_fa.input(update_workspace_state, _state_inputs, _state_outputs)
1318
 
1319
+ # Output list β€” 32 components
1320
  ws_outs = [ws_status, ws_plot, ws_info, ws_ov_msg, ws_conf,
1321
  ws_fs_col, ws_fd_col,
1322
  ws_dr, ws_fs, ws_fr, ws_fa,
 
1327
  ws_flip_reg_info, ws_flip_reg_tag,
1328
  ws_flip_ai_info, ws_flip_ai_tag,
1329
  ws_flip_dual_col, ws_flip_dual_state,
1330
+ ws_flip_reg_sub, ws_flip_ai_sub,
1331
+ ws_plot_fp]
1332
 
1333
  ws_fetch.click(fetch_next, [ws_user], ws_outs)
1334
  ws_submit.click(submit_feedback,
 
1520
  check("0-vote docs served last",
1521
  sorted_docs[2]['num_votes'] == 0 and sorted_docs[3]['num_votes'] == 0)
1522
 
1523
+ # --- 8. Plot Fingerprint / gr.skip() Optimization ---
1524
+ print("\n8. Plot Fingerprint Optimization (_plot_fingerprint)")
1525
+ fp_a = _plot_fingerprint(None, None, True)
1526
+ fp_b = _plot_fingerprint(None, None, True)
1527
+ check("Same inputs β†’ identical fingerprint", fp_a == fp_b)
1528
+ fp_with_flip = _plot_fingerprint({'x': 0.25, 'y': 0.5, 'type': 'max'}, None, True)
1529
+ check("Flip vs no-flip β†’ different fingerprint", fp_with_flip != fp_a)
1530
+ fp_overlap_t = _plot_fingerprint(None, None, True)
1531
+ fp_overlap_f = _plot_fingerprint(None, None, False)
1532
+ check("Overlap True vs False β†’ different fingerprint", fp_overlap_t != fp_overlap_f)
1533
+ # Verify update_workspace_state returns gr.skip() when fingerprint unchanged
1534
+ mock_exp = {
1535
+ 'raw_data': {'measured_data': {'x_values': list(np.linspace(0, 1, 50)),
1536
+ 'y_values': list(np.sin(np.linspace(0, 1, 50)))}},
1537
+ 'reg_params': {'amplitude': 0.5, 'T': 1.0, 'phase': 0.0, 'offset': 0.0},
1538
+ 'ai_params': None, 'filename': 'test'
1539
+ }
1540
+ mock_mp = {'model_classes': {'data_quality': 2, 'regular_fit_quality': 2}}
1541
+ # First call: prev_fp=None β†’ must draw (not skip)
1542
+ result1 = update_workspace_state('Perfect (2)', 'Perfect (2)', None, None,
1543
+ mock_mp, True, mock_exp, None)
1544
+ check("First call (prev_fp=None) β†’ plot is NOT gr.skip()",
1545
+ not isinstance(result1[0], type(gr.skip())))
1546
+ new_fp = result1[-1] # last element is the new fingerprint
1547
+ check("First call returns a fingerprint tuple", isinstance(new_fp, tuple))
1548
+ # Second call: same ratings β†’ fingerprint unchanged β†’ gr.skip()
1549
+ result2 = update_workspace_state('Perfect (2)', 'Perfect (2)', None, None,
1550
+ mock_mp, True, mock_exp, new_fp)
1551
+ is_skip = (result2[0].__class__.__name__ == 'skip'
1552
+ or result2[0] is gr.skip() # Gradio skip sentinel
1553
+ or repr(result2[0]) == repr(gr.skip()))
1554
+ check("Second call (same ratings) β†’ plot IS gr.skip()", is_skip)
1555
+ # Third call: change rating to cross gate β†’ fingerprint changes β†’ redraw
1556
+ result3 = update_workspace_state('Bad (0)', 'Perfect (2)', None, None,
1557
+ mock_mp, True, mock_exp, new_fp)
1558
+ check("Gate-crossing rating change β†’ plot is NOT gr.skip()",
1559
+ not isinstance(result3[0], type(gr.skip()))
1560
+ or repr(result3[0]) != repr(gr.skip()))
1561
+
1562
  print(f"\n{'=' * 60}")
1563
  print(f"RESULTS: {passed} passed, {failed} failed")
1564
  print(f"{'=' * 60}")