Alex W. commited on
Commit
06b76e9
Β·
1 Parent(s): 7ecfc20

ValueError: A function (gen_single_export) didn't return enough output values
(needed: 6, returned: 5).
Output components: [textbox, image, file, file, file, file]
```

Reproduced on HF Space after clicking πŸ–¨οΈ Export PNG / PDF / SVG.

The `outputs` list in both export button wirings has 6 components:

```python
outputs=[single_status, single_preview, # 1: status text
dl_s_png, dl_s_pdf, dl_s_svg, dl_s_zip] # 2: image preview
# 3-6: file downloads
```

But all return paths in `gen_single_export` and `gen_compare_export`
returned only 5 values β€” `single_preview` (the PNG image component)
was in the outputs list but had no corresponding return value.

All return paths in both functions updated from 5 β†’ 6 values.
The PNG path is reused for both the image preview and the PNG download:

```python
return status, png, pdf, svg, zip_p

return status, png, png, pdf, svg, zip_p
```

Early-exit error paths also fixed (None count: 5 β†’ 6):

```python
return "Please select a model.", None, None, None, None

return "Please select a model.", None, None, None, None, None
```

`ui/tab_plot.py` only. 6 return statements updated:

| Function | Line | Change |
|----------|------|--------|
| `gen_single_export` | early exit: no model_id | `NoneΓ—5` β†’ `NoneΓ—6` |
| `gen_single_export` | early exit: df empty | `NoneΓ—5` β†’ `NoneΓ—6` |
| `gen_single_export` | normal return | `5 values` β†’ `6 values` |
| `gen_compare_export` | early exit: same model | `NoneΓ—5` β†’ `NoneΓ—6` |
| `gen_compare_export` | early exit: df empty | `NoneΓ—5` β†’ `NoneΓ—6` |
| `gen_compare_export` | normal return | `5 values` β†’ `6 values` |

No logic changes. No UI changes. No other files touched.

```
outputs list : [status, preview, png, pdf, svg, zip] β†’ 6 βœ…
return (normal): status, png, png, pdf, svg, zip_p β†’ 6 βœ…
return (error) : "msg", None, None, None, None, None β†’ 6 βœ…
```

Files changed (1) hide show
  1. ui/tab_plot.py +96 -94
ui/tab_plot.py CHANGED
@@ -1,9 +1,11 @@
1
  # ui/tab_plot.py
2
  """
3
  Tab5: Plot β€” Publication-quality figure generation
4
- - matplotlib β†’ PNG / PDF / SVG export (300 dpi, paper-ready)
5
- - Plotly native β†’ interactive browser preview (12Γ—1, full-width, fast)
6
- Two completely independent rendering paths, no conversion between them.
 
 
7
  """
8
 
9
  import os
@@ -82,14 +84,13 @@ def _make_zip(paths: list) -> str | None:
82
 
83
  def gen_single_plotly(model_id, modality, start_l, end_l, show_band,
84
  progress=gr.Progress()):
85
- """Fast path: native Plotly, no matplotlib involved."""
86
  if not model_id:
87
  return None, "Please select a model."
88
  progress(0.2, desc="Loading data from DB...")
89
  df = _load_df(model_id, modality, start_l, end_l)
90
  if df.empty:
91
  return None, f"No data for {model_id}. Run Tab 2 analysis first."
92
- progress(0.7, desc="Building interactive figure...")
93
  fig = plotly_single(df, _short(model_id), show_band=show_band)
94
  status = (
95
  f"βœ… {model_id} | {df['layer'].nunique()} layers "
@@ -101,33 +102,35 @@ def gen_single_plotly(model_id, modality, start_l, end_l, show_band,
101
 
102
  def gen_single_export(model_id, modality, start_l, end_l, show_band,
103
  progress=gr.Progress()):
104
- """Export path: matplotlib β†’ PNG/PDF/SVG."""
105
  if not model_id:
106
- return "Please select a model.", None, None, None, None
107
  progress(0.15, desc="Loading data from DB...")
108
  df = _load_df(model_id, modality, start_l, end_l)
109
  if df.empty:
110
- return f"No data for {model_id}.", None, None, None, None
111
  head_dim, d_model = _infer_dims(df)
112
- progress(0.40, desc="Rendering matplotlib figure (18x20 in, 300 dpi)...")
113
  import matplotlib.pyplot as plt
114
- fig = plot_single_model(df, _short(model_id),
115
- show_band=show_band,
116
- head_dim=head_dim, d_model=d_model)
117
- progress(0.75, desc="Saving PNG / PDF / SVG...")
 
 
118
  base = _safe_path(f"single_{_short(model_id)}_L{int(start_l)}-{int(end_l)}")
119
  paths = save_figure(fig, base)
120
  plt.close(fig)
121
- zip_p = _make_zip(paths)
122
  status = (
123
- f"Exported: {', '.join(os.path.basename(p) for p in paths)}\n"
124
- f"head_dim={head_dim} d_model={d_model}"
125
  )
126
  progress(1.0)
127
  png = paths[0] if len(paths) > 0 else None
128
  pdf = paths[1] if len(paths) > 1 else None
129
  svg = paths[2] if len(paths) > 2 else None
130
- return status, png, pdf, svg, zip_p
 
131
 
132
 
133
  # ─────────────────────────────────────────────────────────────────────────────
@@ -148,7 +151,7 @@ def gen_compare_plotly(model_a, model_b, modality, start_l, end_l,
148
  return None, f"No data for Model A ({model_a})."
149
  if df_b.empty:
150
  return None, f"No data for Model B ({model_b})."
151
- progress(0.65, desc="Building interactive comparison figure...")
152
  fig = plotly_compare(df_a, df_b, _short(model_a), _short(model_b),
153
  show_band=show_band, show_delta=show_delta)
154
  status = (
@@ -162,12 +165,12 @@ def gen_compare_plotly(model_a, model_b, modality, start_l, end_l,
162
  def gen_compare_export(model_a, model_b, modality, start_l, end_l,
163
  show_band, show_delta, progress=gr.Progress()):
164
  if not model_a or not model_b or model_a == model_b:
165
- return "Select two different models.", None, None, None, None
166
  progress(0.10, desc="Loading data...")
167
  df_a = _load_df(model_a, modality, start_l, end_l)
168
  df_b = _load_df(model_b, modality, start_l, end_l)
169
  if df_a.empty or df_b.empty:
170
- return "Missing data for one or both models.", None, None, None, None
171
  head_dim_a, d_model_a = _infer_dims(df_a)
172
  head_dim_b, d_model_b = _infer_dims(df_b)
173
  head_dim = (head_dim_a + head_dim_b) // 2
@@ -185,20 +188,21 @@ def gen_compare_export(model_a, model_b, modality, start_l, end_l,
185
  )
186
  paths = save_figure(fig, base)
187
  plt.close(fig)
188
- zip_p = _make_zip(paths)
189
  status = (
190
- f"Exported: {', '.join(os.path.basename(p) for p in paths)}\n"
191
- f"head_dimβ‰ˆ{head_dim} d_modelβ‰ˆ{d_model}"
192
  )
193
  progress(1.0)
194
  png = paths[0] if len(paths) > 0 else None
195
  pdf = paths[1] if len(paths) > 1 else None
196
  svg = paths[2] if len(paths) > 2 else None
197
- return status, png, pdf, svg, zip_p
 
198
 
199
 
200
  # ─────────────────────────────────────────────────────────────────────────────
201
- # Tab5 UI
202
  # ─────────────────────────────────────────────────────────────────────────────
203
 
204
  def build_tab_plot():
@@ -206,11 +210,10 @@ def build_tab_plot():
206
  gr.Markdown("""
207
  ## Wang's Five Laws β€” Figures
208
 
209
- Two independent rendering paths:
210
- | Path | Engine | Speed | Use for |
211
- |------|--------|-------|---------|
212
- | **Interactive** | Native Plotly, 12Γ—1 full-width | Fast | Exploration, hover, zoom |
213
- | **Export** | Matplotlib 18Γ—20 in @ 300 dpi | Slower | Paper submission (PNG/PDF/SVG) |
214
 
215
  > Run **Tab 2 (Analyze)** first to populate the database.
216
  """)
@@ -224,13 +227,14 @@ def build_tab_plot():
224
  start_l = gr.Number(value=0, precision=0, label="Start Layer", scale=1)
225
  end_l = gr.Number(value=47, precision=0, label="End Layer", scale=1)
226
  show_band_chk = gr.Checkbox(
227
- value=True, label="Show IQR band (head consistency)", scale=1
228
  )
229
 
230
  gr.Markdown("---")
231
 
232
  # ══ Single model ══════════════════════════════════════════════════════
233
  with gr.Accordion("πŸ“Š Single Model", open=True):
 
234
  choices = _get_model_choices()
235
  single_model = gr.Dropdown(
236
  choices=choices,
@@ -240,39 +244,38 @@ def build_tab_plot():
240
  info="Refresh page after new analysis to update this list.",
241
  )
242
 
243
- with gr.Tabs():
244
- # ── Interactive ───────────────────────────────────────────────
245
- with gr.Tab("πŸ“‰ Interactive (Plotly)"):
246
- single_plotly_btn = gr.Button(
247
- "⚑ Generate Interactive Figure", variant="primary"
248
- )
249
- single_plotly_status = gr.Textbox(
250
- lines=1, interactive=False, label="Status"
251
- )
252
- single_plotly_fig = gr.Plot(label=None)
253
-
254
- # ── Export ────────────────────────────────────────────────────
255
- with gr.Tab("πŸ–¨οΈ Export (PNG / PDF / SVG)"):
256
- single_export_btn = gr.Button(
257
- "πŸ–¨οΈ Render & Export (paper-quality, ~30 s)",
258
- variant="secondary"
259
- )
260
- single_export_status = gr.Textbox(
261
- lines=2, interactive=False, label="Export status"
262
- )
263
- single_preview = gr.Image(
264
- type="filepath", label="PNG preview", height=400
265
- )
266
- with gr.Row():
267
- dl_s_png = gr.File(label="⬇ PNG (300 dpi)")
268
- dl_s_pdf = gr.File(label="⬇ PDF (vector)")
269
- dl_s_svg = gr.File(label="⬇ SVG (vector)")
270
- dl_s_zip = gr.File(label="⬇ ZIP (all)")
271
 
272
  gr.Markdown("---")
273
 
274
  # ══ Two-model comparison ══════════════════════════════════════════════
275
  with gr.Accordion("πŸ“Š Two-Model Comparison", open=False):
 
276
  with gr.Row():
277
  model_a = gr.Dropdown(
278
  choices=choices,
@@ -290,66 +293,65 @@ def build_tab_plot():
290
  value=True, label="Show Ξ” fill (B βˆ’ A)", scale=1
291
  )
292
 
293
- with gr.Tabs():
294
- with gr.Tab("πŸ“‰ Interactive (Plotly)"):
295
- cmp_plotly_btn = gr.Button(
296
- "⚑ Generate Interactive Comparison", variant="primary"
297
- )
298
- cmp_plotly_status = gr.Textbox(
299
- lines=1, interactive=False, label="Status"
300
- )
301
- cmp_plotly_fig = gr.Plot(label=None)
302
-
303
- with gr.Tab("πŸ–¨οΈ Export (PNG / PDF / SVG)"):
304
- cmp_export_btn = gr.Button(
305
- "πŸ–¨οΈ Render & Export", variant="secondary"
306
- )
307
- cmp_export_status = gr.Textbox(
308
- lines=2, interactive=False, label="Export status"
309
- )
310
- cmp_preview = gr.Image(
311
- type="filepath", label="PNG preview", height=400
312
- )
313
- with gr.Row():
314
- dl_c_png = gr.File(label="⬇ PNG (300 dpi)")
315
- dl_c_pdf = gr.File(label="⬇ PDF (vector)")
316
- dl_c_svg = gr.File(label="⬇ SVG (vector)")
317
- dl_c_zip = gr.File(label="⬇ ZIP (all)")
318
 
319
  gr.Markdown("""
320
  ---
321
  **Reading the figures**
322
- - **IQR band** = 25%–75% quantile across attention heads per layer.
323
- Narrow band β†’ heads behave consistently β†’ model is well-organized.
324
- - **Dotted vertical lines** = global (K=V shared) layers (Gemma-4 only).
325
- - **Dashed horizontal lines** = theoretical ideals (r=1, SSR=0, Ξ±=1)
326
  or random baselines (cosU: 1/√d_head Β· cosV: 1/√d_model).
327
- - **Super-orthogonality** (Law 4): cosU(Q–V) and cosU(K–V) sit *below*
328
- the random baseline β€” pretraining actively pushes V away from Q/K.
329
  """)
330
 
331
  # ── Wiring ────────────────────────────────────────────────────────────
 
332
  single_plotly_btn.click(
333
  fn=gen_single_plotly,
334
  inputs=[single_model, modality_sel, start_l, end_l, show_band_chk],
335
- outputs=[single_plotly_fig, single_plotly_status],
336
  )
337
  single_export_btn.click(
338
  fn=gen_single_export,
339
  inputs=[single_model, modality_sel, start_l, end_l, show_band_chk],
340
- outputs=[single_export_status, single_preview,
341
  dl_s_png, dl_s_pdf, dl_s_svg, dl_s_zip],
342
  )
343
  cmp_plotly_btn.click(
344
  fn=gen_compare_plotly,
345
  inputs=[model_a, model_b, modality_sel,
346
  start_l, end_l, show_band_chk, show_delta_chk],
347
- outputs=[cmp_plotly_fig, cmp_plotly_status],
348
  )
349
  cmp_export_btn.click(
350
  fn=gen_compare_export,
351
  inputs=[model_a, model_b, modality_sel,
352
  start_l, end_l, show_band_chk, show_delta_chk],
353
- outputs=[cmp_export_status, cmp_preview,
354
  dl_c_png, dl_c_pdf, dl_c_svg, dl_c_zip],
355
  )
 
1
  # ui/tab_plot.py
2
  """
3
  Tab5: Plot β€” Publication-quality figure generation
4
+ - Plotly native β†’ interactive browser preview (12Γ—1, full-width, fast)
5
+ - matplotlib β†’ PNG / PDF / SVG export (300 dpi, paper-ready)
6
+
7
+ NO nested gr.Tabs() β€” avoids Gradio rendering bugs with Accordion+Tabs nesting.
8
+ Two side-by-side buttons instead: ⚑ Interactive | πŸ–¨οΈ Export
9
  """
10
 
11
  import os
 
84
 
85
  def gen_single_plotly(model_id, modality, start_l, end_l, show_band,
86
  progress=gr.Progress()):
 
87
  if not model_id:
88
  return None, "Please select a model."
89
  progress(0.2, desc="Loading data from DB...")
90
  df = _load_df(model_id, modality, start_l, end_l)
91
  if df.empty:
92
  return None, f"No data for {model_id}. Run Tab 2 analysis first."
93
+ progress(0.7, desc="Building Plotly figure...")
94
  fig = plotly_single(df, _short(model_id), show_band=show_band)
95
  status = (
96
  f"βœ… {model_id} | {df['layer'].nunique()} layers "
 
102
 
103
  def gen_single_export(model_id, modality, start_l, end_l, show_band,
104
  progress=gr.Progress()):
 
105
  if not model_id:
106
+ return "Please select a model.", None, None, None, None, None
107
  progress(0.15, desc="Loading data from DB...")
108
  df = _load_df(model_id, modality, start_l, end_l)
109
  if df.empty:
110
+ return f"No data for {model_id}.", None, None, None, None, None
111
  head_dim, d_model = _infer_dims(df)
112
+ progress(0.40, desc="Rendering matplotlib figure (18Γ—20 in, 300 dpi)...")
113
  import matplotlib.pyplot as plt
114
+ fig = plot_single_model(
115
+ df, _short(model_id),
116
+ show_band=show_band,
117
+ head_dim=head_dim, d_model=d_model,
118
+ )
119
+ progress(0.78, desc="Saving PNG / PDF / SVG...")
120
  base = _safe_path(f"single_{_short(model_id)}_L{int(start_l)}-{int(end_l)}")
121
  paths = save_figure(fig, base)
122
  plt.close(fig)
123
+ zip_p = _make_zip(paths)
124
  status = (
125
+ f"βœ… Exported: {', '.join(os.path.basename(p) for p in paths)}\n"
126
+ f" head_dim={head_dim} d_model={d_model}"
127
  )
128
  progress(1.0)
129
  png = paths[0] if len(paths) > 0 else None
130
  pdf = paths[1] if len(paths) > 1 else None
131
  svg = paths[2] if len(paths) > 2 else None
132
+ # 6 values: status, preview(=png), png_dl, pdf_dl, svg_dl, zip
133
+ return status, png, png, pdf, svg, zip_p
134
 
135
 
136
  # ─────────────────────────────────────────────────────────────────────────────
 
151
  return None, f"No data for Model A ({model_a})."
152
  if df_b.empty:
153
  return None, f"No data for Model B ({model_b})."
154
+ progress(0.65, desc="Building Plotly comparison figure...")
155
  fig = plotly_compare(df_a, df_b, _short(model_a), _short(model_b),
156
  show_band=show_band, show_delta=show_delta)
157
  status = (
 
165
  def gen_compare_export(model_a, model_b, modality, start_l, end_l,
166
  show_band, show_delta, progress=gr.Progress()):
167
  if not model_a or not model_b or model_a == model_b:
168
+ return "Select two different models.", None, None, None, None, None
169
  progress(0.10, desc="Loading data...")
170
  df_a = _load_df(model_a, modality, start_l, end_l)
171
  df_b = _load_df(model_b, modality, start_l, end_l)
172
  if df_a.empty or df_b.empty:
173
+ return "Missing data for one or both models.", None, None, None, None, None
174
  head_dim_a, d_model_a = _infer_dims(df_a)
175
  head_dim_b, d_model_b = _infer_dims(df_b)
176
  head_dim = (head_dim_a + head_dim_b) // 2
 
188
  )
189
  paths = save_figure(fig, base)
190
  plt.close(fig)
191
+ zip_p = _make_zip(paths)
192
  status = (
193
+ f"βœ… Exported: {', '.join(os.path.basename(p) for p in paths)}\n"
194
+ f" head_dimβ‰ˆ{head_dim} d_modelβ‰ˆ{d_model}"
195
  )
196
  progress(1.0)
197
  png = paths[0] if len(paths) > 0 else None
198
  pdf = paths[1] if len(paths) > 1 else None
199
  svg = paths[2] if len(paths) > 2 else None
200
+ # 6 values: status, preview(=png), png_dl, pdf_dl, svg_dl, zip
201
+ return status, png, png, pdf, svg, zip_p
202
 
203
 
204
  # ─────────────────────────────────────────────────────────────────────────────
205
+ # Tab5 UI β€” NO nested gr.Tabs() inside Accordion
206
  # ─────────────────────────────────────────────────────────────────────────────
207
 
208
  def build_tab_plot():
 
210
  gr.Markdown("""
211
  ## Wang's Five Laws β€” Figures
212
 
213
+ | Button | Engine | Speed | Output |
214
+ |--------|--------|-------|--------|
215
+ | ⚑ **Interactive** | Native Plotly 12Γ—1 full-width | ~2 s | In-page, hover/zoom |
216
+ | πŸ–¨οΈ **Export** | Matplotlib 18Γ—20 in @ 300 dpi | ~30 s | PNG Β· PDF Β· SVG download |
 
217
 
218
  > Run **Tab 2 (Analyze)** first to populate the database.
219
  """)
 
227
  start_l = gr.Number(value=0, precision=0, label="Start Layer", scale=1)
228
  end_l = gr.Number(value=47, precision=0, label="End Layer", scale=1)
229
  show_band_chk = gr.Checkbox(
230
+ value=True, label="Show IQR band", scale=1
231
  )
232
 
233
  gr.Markdown("---")
234
 
235
  # ══ Single model ══════════════════════════════════════════════════════
236
  with gr.Accordion("πŸ“Š Single Model", open=True):
237
+
238
  choices = _get_model_choices()
239
  single_model = gr.Dropdown(
240
  choices=choices,
 
244
  info="Refresh page after new analysis to update this list.",
245
  )
246
 
247
+ # Two side-by-side buttons β€” no nested Tabs
248
+ with gr.Row():
249
+ single_plotly_btn = gr.Button(
250
+ "⚑ Interactive (Plotly)", variant="primary", scale=1
251
+ )
252
+ single_export_btn = gr.Button(
253
+ "πŸ–¨οΈ Export PNG / PDF / SVG", variant="secondary", scale=1
254
+ )
255
+
256
+ single_status = gr.Textbox(
257
+ lines=2, interactive=False, label="Status"
258
+ )
259
+
260
+ # Interactive output β€” always visible, populated on demand
261
+ single_plotly_fig = gr.Plot(label="Interactive figure")
262
+
263
+ # Export outputs β€” always visible, populated on demand
264
+ gr.Markdown("#### πŸ–¨οΈ Export outputs")
265
+ single_preview = gr.Image(
266
+ type="filepath", label="PNG preview (click to enlarge)", height=350
267
+ )
268
+ with gr.Row():
269
+ dl_s_png = gr.File(label="⬇ PNG (300 dpi)")
270
+ dl_s_pdf = gr.File(label="⬇ PDF (vector)")
271
+ dl_s_svg = gr.File(label="⬇ SVG (vector)")
272
+ dl_s_zip = gr.File(label="⬇ ZIP (all formats)")
 
 
273
 
274
  gr.Markdown("---")
275
 
276
  # ══ Two-model comparison ══════════════════════════════════════════════
277
  with gr.Accordion("πŸ“Š Two-Model Comparison", open=False):
278
+
279
  with gr.Row():
280
  model_a = gr.Dropdown(
281
  choices=choices,
 
293
  value=True, label="Show Ξ” fill (B βˆ’ A)", scale=1
294
  )
295
 
296
+ with gr.Row():
297
+ cmp_plotly_btn = gr.Button(
298
+ "⚑ Interactive (Plotly)", variant="primary", scale=1
299
+ )
300
+ cmp_export_btn = gr.Button(
301
+ "πŸ–¨οΈ Export PNG / PDF / SVG", variant="secondary", scale=1
302
+ )
303
+
304
+ cmp_status = gr.Textbox(
305
+ lines=2, interactive=False, label="Status"
306
+ )
307
+
308
+ cmp_plotly_fig = gr.Plot(label="Interactive comparison figure")
309
+
310
+ gr.Markdown("#### πŸ–¨οΈ Export outputs")
311
+ cmp_preview = gr.Image(
312
+ type="filepath", label="PNG preview", height=350
313
+ )
314
+ with gr.Row():
315
+ dl_c_png = gr.File(label="⬇ PNG (300 dpi)")
316
+ dl_c_pdf = gr.File(label="⬇ PDF (vector)")
317
+ dl_c_svg = gr.File(label="⬇ SVG (vector)")
318
+ dl_c_zip = gr.File(label="⬇ ZIP (all formats)")
 
 
319
 
320
  gr.Markdown("""
321
  ---
322
  **Reading the figures**
323
+ - **IQR band** β€” 25%–75% quantile across attention heads per layer.
324
+ Narrow band β†’ heads are consistent β†’ model is well-organized.
325
+ - **Dotted vertical lines** β€” global (K=V shared) layers (Gemma-4 only).
326
+ - **Dashed horizontal lines** β€” theoretical ideals (r=1, SSR=0, Ξ±=1)
327
  or random baselines (cosU: 1/√d_head Β· cosV: 1/√d_model).
328
+ - **Super-orthogonality** (Law 4) β€” cosU(Q–V) and cosU(K–V) sit *below*
329
+ the random baseline; pretraining actively pushes V away from Q/K.
330
  """)
331
 
332
  # ── Wiring ────────────────────────────────────────────────────────────
333
+
334
  single_plotly_btn.click(
335
  fn=gen_single_plotly,
336
  inputs=[single_model, modality_sel, start_l, end_l, show_band_chk],
337
+ outputs=[single_plotly_fig, single_status],
338
  )
339
  single_export_btn.click(
340
  fn=gen_single_export,
341
  inputs=[single_model, modality_sel, start_l, end_l, show_band_chk],
342
+ outputs=[single_status, single_preview,
343
  dl_s_png, dl_s_pdf, dl_s_svg, dl_s_zip],
344
  )
345
  cmp_plotly_btn.click(
346
  fn=gen_compare_plotly,
347
  inputs=[model_a, model_b, modality_sel,
348
  start_l, end_l, show_band_chk, show_delta_chk],
349
+ outputs=[cmp_plotly_fig, cmp_status],
350
  )
351
  cmp_export_btn.click(
352
  fn=gen_compare_export,
353
  inputs=[model_a, model_b, modality_sel,
354
  start_l, end_l, show_band_chk, show_delta_chk],
355
+ outputs=[cmp_status, cmp_preview,
356
  dl_c_png, dl_c_pdf, dl_c_svg, dl_c_zip],
357
  )