Skip to content

Commit 27a17ed

Browse files
VarShankarclaude
andcommitted
FastOL: full benchmark names; bold best results in the generated tables
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 0b3e34a commit 27a17ed

3 files changed

Lines changed: 88 additions & 53 deletions

File tree

‎FastOLPaper/source/build_gpu_evaluation_tables.py‎

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@
1818
EVID = HERE.parent / "evidence" / "gpu_evaluation" if len(sys.argv) < 2 else Path(sys.argv[1])
1919
SEC = HERE / "sections"
2020
SEC.mkdir(exist_ok=True)
21-
CASES = [("forced_turb", "Forced turbulence"), ("species_transport", "Species transport"),
22-
("merge_vortices_easier", "Merging vortices"), ("lid_cavity_flow", "Lid cavity"),
23-
("flow_cylinder_laminar", "Cylinder, steady"), ("flow_cylinder_shedding", "Cylinder, shedding"),
24-
("backward_facing_step", "Backward-facing step"), ("buoyancy_cavity_flow", "Buoyancy cavity"),
25-
("taylor_green", "Taylor--Green (space)"), ("taylor_green_time", "Taylor--Green (time)")]
21+
CASES = [("forced_turb", "Forced isotropic turbulence"), ("species_transport", "Turbulent species transport"),
22+
("merge_vortices_easier", "Merging vortices"), ("lid_cavity_flow", "Lid-driven cavity"),
23+
("flow_cylinder_laminar", "Cylinder flow (steady)"), ("flow_cylinder_shedding", "Cylinder flow (vortex shedding)"),
24+
("backward_facing_step", "Backward-facing step"), ("buoyancy_cavity_flow", "Buoyancy-driven cavity"),
25+
("taylor_green", "Taylor--Green vortices (space)"), ("taylor_green_time", "Taylor--Green vortices (spacetime)")]
2626

2727

2828
def sci(x, digits=1):
@@ -33,6 +33,18 @@ def sci(x, digits=1):
3333
return f"${m:.{digits}f}\\times10^{{{e}}}$"
3434

3535

36+
def bold(cell):
37+
"""Bold a math cell "$...$", keeping any text after it."""
38+
i = cell.index("$", 1)
39+
return r"$\mathbf{" + cell[1:i] + "}$" + cell[i + 1:]
40+
41+
42+
def bold_best(cells, values, best=min):
43+
"""Bold every cell whose printed value equals that of the best value (ties at the printed precision)."""
44+
target = cells[values.index(best(values))]
45+
return [bold(c) if c == target else c for c in cells]
46+
47+
3648
def load(name):
3749
p = EVID / name
3850
return [json.loads(l) for l in p.read_text().splitlines() if l.strip()] if p.exists() else []
@@ -78,13 +90,15 @@ def main():
7890
if not rs:
7991
continue
8092
gmax = max(r["global_direct"] / r["global_lagrange"] for r in by[case])
81-
arows.append(f"{name} & {sci(rs[0]['alpha_global'])} & ${rs[0]['alpha_local']:.0f}$ & ${gmax:.1f}$ \\\\")
93+
ag, al = bold_best([sci(rs[0]['alpha_global']), f"${rs[0]['alpha_local']:.0f}$"],
94+
[rs[0]['alpha_global'], rs[0]['alpha_local']])
95+
arows.append(f"{name} & {ag} & {al} & ${gmax:.1f}$ \\\\")
8296
amp = [r"\begin{table}[pos=tbp]", r"\centering\small", r"\setlength{\tabcolsep}{4pt}",
8397
r"\caption{Amplification factor $\alpha_{\rm spatial}=\|B\|_F\|(\bA^\star)^{\mathsf T}\|_F/\|B(\bA^\star)^{\mathsf T}\|_F$ "
8498
r"of~\eqref{eq:spatial-amplification}, which measures the cancellation among the spatial coefficients, for $N=1000$ "
8599
r"training fields, for the global and for our local output reconstruction (stencils combined as in "
86100
r"Section~\ref{sec:lagrange_accuracy}), and the largest ratio of the direct to the Lagrange error with global "
87-
r"output reconstruction from Table~\ref{tab:lagrange-all}.}",
101+
r"output reconstruction from Table~\ref{tab:lagrange-all}. Bold: smaller amplification factor in each row.}",
88102
r"\label{tab:amplification}", r"\begin{tabular}{lrrr}", r"\toprule",
89103
r"Data set & Global $\alpha_{\rm spatial}$ & Local $\alpha_{\rm spatial}$ & Largest gain (global) \\", r"\midrule",
90104
*arows, r"\bottomrule", r"\end{tabular}", r"\end{table}", ""]
@@ -93,14 +107,16 @@ def main():
93107
# ---- lid-cavity matched control (validated fixed-shape model, global reconstruction)
94108
crow = []
95109
for r in sorted((r for r in by.get("lid_cavity_flow", []) if r["model"] == "fixed_selected"), key=lambda r: r["N"]):
96-
crow.append(f"{r['N']} & {sci(r['global_direct'], 2)} & {sci(r['global_lagrange'], 2)} & "
97-
f"{r['global_direct'] / r['global_lagrange']:.2f} & {sci(r['global_direct_samples'], 2)} & "
110+
ev = [r['global_direct'], r['global_lagrange'], r['global_direct_samples']]
111+
d, lg, ds = bold_best([sci(v, 2) for v in ev], ev)
112+
crow.append(f"{r['N']} & {d} & {lg} & "
113+
f"{r['global_direct'] / r['global_lagrange']:.2f} & {ds} & "
98114
f"{sci(r['global_coeff_norm'], 2)} \\\\")
99115
ctl = [r"\begin{table}[pos=!htbp]", r"\centering\small",
100116
r"\caption{Test error at the nodes (mean relative velocity error over the 100 test inputs) of direct and Lagrange evaluation of the validated "
101-
r"unaugmented fixed-shape lid-cavity model with global output reconstruction, of direct evaluation applied to the "
117+
r"unaugmented fixed-shape model of the lid-driven cavity with global output reconstruction, of direct evaluation applied to the "
102118
r"sampled velocities instead of the coefficients, and the Frobenius norm of the spatial coefficients of the "
103-
r"training fields.}",
119+
r"training fields. Bold: smallest test error in each row.}",
104120
r"\label{tab:lagrange-matched-control}", r"\begin{tabular}{rrrrrr}", r"\toprule",
105121
r"$N$ & Direct & Lagrange & Direct/Lagrange & Direct on samples & $\|B\|_F$ \\", r"\midrule", *crow,
106122
r"\bottomrule", r"\end{tabular}", r"\end{table}", ""]
@@ -113,17 +129,17 @@ def main():
113129
if r is None:
114130
continue
115131
assert r["N"] == 10000 and "global" in r, (case, r["N"])
132+
lv = r["local_multiscale"] if "local_multiscale" in r else r["local_default"]
133+
glob, loc = bold_best([sci(r["global"], 2), sci(lv, 2)], [r["global"], lv])
116134
if "local_multiscale" in r:
117-
loc = sci(r["local_multiscale"], 2) + " (multiscale)"
118-
else:
119-
loc = sci(r["local_default"], 2)
120-
srow.append(f"{name} & {r['m']} & {r['held_out']} & {sci(r['global'], 2)} & {loc} \\\\")
135+
loc += " (multiscale)"
136+
srow.append(f"{name} & {r['m']} & {r['held_out']} & {glob} & {loc} \\\\")
121137
st = [r"\begin{table}[pos=tbp]", r"\centering\small", r"\setlength{\tabcolsep}{4pt}",
122138
r"\caption{Test error between the nodes. We reconstruct the predicted nodal velocities of the 100 test inputs "
123139
r"(model with the smallest validation error at $N=10000$) and measure the mean relative velocity error at the "
124140
r"evaluation points, the mesh points of each data set that are not among its $m$ nodes. Global: the divergence-free Mat\'ern interpolant of~\citet{sharma2026fluids} on all "
125141
r"nodes. Local: our output reconstruction with the cubic PHS, or with the multiscale Mat\'ern kernel ($c_\sigma=16$) "
126-
r"where marked (Appendix~\ref{app:multiscale-spatial} compares it with the PHS).}",
142+
r"where marked (Appendix~\ref{app:multiscale-spatial} compares it with the PHS). Bold: smaller error in each row.}",
127143
r"\label{tab:spatial-reconstruction-controls}", r"\begin{tabular}{lrrrr}", r"\toprule",
128144
r"Data set & Nodes $m$ & Evaluation points & Global & Local \\", r"\midrule", *srow,
129145
r"\bottomrule", r"\end{tabular}", r"\end{table}", ""]
@@ -133,7 +149,7 @@ def main():
133149
figs = []
134150
ticks = {"species_transport": "xtick={0.003,0.01,0.03,0.1}, ytick={200,300,500,800}, ",
135151
"forced_turb": "xtick={0.7,1,2,3}, ytick={0.5,0.7,1,1.4}, "}
136-
for case, title in (("species_transport", "Species transport"), ("forced_turb", "Forced turbulence")):
152+
for case, title in (("species_transport", "Turbulent species transport"), ("forced_turb", "Forced isotropic turbulence")):
137153
r = spa.get(case)
138154
if not r or "structure_functions" not in r:
139155
continue

‎FastOLPaper/source/build_gpu_results.py‎

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,16 @@
4444
models = ["fixed_no_poly", "trainable_no_poly", "fixed_affine", "trainable_affine"]
4545
NVALUES = [500, 1000, 5000, 10000]
4646
CASES = [
47-
("forced_turb", "Forced turbulence"),
48-
("species_transport", "Species transport"),
47+
("forced_turb", "Forced isotropic turbulence"),
48+
("species_transport", "Turbulent species transport"),
4949
("merge_vortices_easier", "Merging vortices"),
50-
("lid_cavity_flow", "Lid cavity"),
51-
("flow_cylinder_laminar", "Cylinder, steady"),
52-
("flow_cylinder_shedding", "Cylinder, shedding"),
53-
("taylor_green", "Taylor--Green (space)"),
54-
("taylor_green_time", "Taylor--Green (time)"),
50+
("lid_cavity_flow", "Lid-driven cavity"),
51+
("flow_cylinder_laminar", "Cylinder flow (steady)"),
52+
("flow_cylinder_shedding", "Cylinder flow (vortex shedding)"),
53+
("taylor_green", "Taylor--Green vortices (space)"),
54+
("taylor_green_time", "Taylor--Green vortices (spacetime)"),
5555
("backward_facing_step", "Backward-facing step"),
56-
("buoyancy_cavity_flow", "Buoyancy cavity"),
56+
("buoyancy_cavity_flow", "Buoyancy-driven cavity"),
5757
]
5858
lookup = {(row["case"], int(row["N"]), row["model"]): row for row in rows}
5959
assert len(lookup) == 160
@@ -195,34 +195,44 @@ def fnum(value, digits=3):
195195
return rf"${coefficient:.{digits-1}f}\times10^{{{exponent}}}$"
196196
return f"{v:.{digits}g}"
197197

198+
def bold(cell):
199+
"""Bold one table cell (math or text)."""
200+
if cell.startswith("$") and cell.endswith("$") and cell.count("$") == 2:
201+
return r"$\mathbf{" + cell[1:-1] + "}$"
202+
return r"\textbf{" + cell + "}"
203+
204+
def bold_best(cells, values, best=min):
205+
"""Bold every cell whose printed value equals that of the best value (ties at the printed precision)."""
206+
target = cells[values.index(best(values))]
207+
return [bold(c) if c == target else c for c in cells]
208+
198209
def metric_table(metric, filename, label, caption, multiplier=1., digits=3):
199210
lines = [r"\begin{table}[pos=!htbp]", r"\centering\small",
200211
r"\setlength{\tabcolsep}{4pt}", r"\caption{"+caption+"}",
201212
r"\label{"+label+"}", r"\begin{tabular}{lrrrr}", r"\toprule",
202213
r"Data set & Fixed & Fitted & Fixed + affine & Fitted + affine \\", r"\midrule"]
203214
for case, title in CASES:
204215
vals = [float(lookup[case,10000,m][metric])*multiplier for m in models]
205-
lines.append(title+" & "+" & ".join(fnum(v,digits) for v in vals)+r" \\")
216+
lines.append(title+" & "+" & ".join(bold_best([fnum(v,digits) for v in vals], vals))+r" \\")
206217
lines.extend([r"\bottomrule",r"\end{tabular}",r"\end{table}"])
207218
path=SOURCE/"sections"/filename
208219
path.write_text("\n".join(lines)+"\n",encoding="utf-8")
209220
outputs.append(path)
210221

211222
metric_table("test_error", "gpu_accuracy_n10000.tex", "tab:gpu-accuracy-n10000",
212-
"Test error (mean relative velocity error of the predictions for the 100 test inputs) at $N=10000$. The two augmented models use the same affine columns. Each model is fitted and validated separately, and all four use the same local output reconstruction. Both Taylor--Green cases use the amplitude normalization~\\eqref{eq:tg-response-transform}.")
223+
"Test error (mean relative velocity error of the predictions for the 100 test inputs) at $N=10000$. The two augmented models use the same affine columns. Each model is fitted and validated separately, and all four use the same local output reconstruction. Both Taylor--Green cases use the amplitude normalization~\\eqref{eq:tg-response-transform}. Bold: smallest test error in each row.")
213224

214225
# Compact full-suite timing/storage table at the largest N. Each cell gives
215226
# accounted decimal GB / synchronized warm ms for 100 fields.
216227
lines=[r"\begin{table}[pos=!htbp]",r"\centering\small",r"\setlength{\tabcolsep}{3pt}",
217-
r"\caption{Accounted storage in decimal GB / median warm prediction time in ms for 100 fields at $N=10000$. Storage includes the stored model arrays, the solve buffers, and cuDSS's estimate of the memory of its factors. It is not measured peak GPU memory. Times include operator Lagrange evaluation and local output reconstruction.}",
228+
r"\caption{Accounted storage in decimal GB / median warm prediction time in ms for 100 fields at $N=10000$. Storage includes the stored model arrays, the solve buffers, and cuDSS's estimate of the memory of its factors. It is not measured peak GPU memory. Times include operator Lagrange evaluation and local output reconstruction. Bold: smallest storage and smallest time in each row.}",
218229
r"\label{tab:gpu-cost-n10000}",r"\begin{tabular}{lrrrr}",r"\toprule",
219230
r"Data set & Fixed & Fitted & Fixed + affine & Fitted + affine \\",r"\midrule"]
220231
for case,title in CASES:
221-
vals=[]
222-
for model in models:
223-
row=lookup[case,10000,model]
224-
vals.append(f"{float(row['accounted_resident_storage_ESTIMATE_decimal_GB']):.3f} / {1000*float(row['warm_100query_median_seconds']):.1f}")
225-
lines.append(title+" & "+" & ".join(vals)+r" \\")
232+
gb=[float(lookup[case,10000,m]['accounted_resident_storage_ESTIMATE_decimal_GB']) for m in models]
233+
ms=[1000*float(lookup[case,10000,m]['warm_100query_median_seconds']) for m in models]
234+
gbc=bold_best([f"{v:.3f}" for v in gb], gb); msc=bold_best([f"{v:.1f}" for v in ms], ms)
235+
lines.append(title+" & "+" & ".join(f"{a} / {b}" for a,b in zip(gbc,msc))+r" \\")
226236
lines += [r"\bottomrule",r"\end{tabular}",r"\end{table}"]
227237
path=SOURCE/"sections"/"gpu_cost_n10000.tex";path.write_text("\n".join(lines)+"\n",encoding="utf-8");outputs.append(path)
228238

@@ -232,27 +242,36 @@ def metric_table(metric, filename, label, caption, multiplier=1., digits=3):
232242
short={"fixed_no_poly":"Fixed shape", "trainable_no_poly":"Fitted shape", "fixed_affine":"Fixed shape + affine", "trainable_affine":"Fitted shape + affine"}
233243
for index,(case,title) in enumerate(CASES):
234244
lines += [r"\begin{table}[pos=!htbp]",r"\centering\small",r"\setlength{\tabcolsep}{3.5pt}",
235-
r"\caption{"+title+r": all models. Test error is the mean relative velocity error of the predictions for the 100 test inputs. Density is $d_K=\mathrm{nnz}(K_{UU})/N^2$ in percent. Factor is the number of entries of the cuDSS Cholesky factor in millions. GB is the accounted storage and ms the median warm time for all 100 fields.}",
245+
r"\caption{"+title+r": all models. Test error is the mean relative velocity error of the predictions for the 100 test inputs. Density is $d_K=\mathrm{nnz}(K_{UU})/N^2$ in percent. Factor is the number of entries of the cuDSS Cholesky factor in millions. GB is the accounted storage and ms the median warm time for all 100 fields. Bold: smallest test error at each $N$.}",
236246
rf"\label{{tab:gpu-all-{case.replace('_','-')}}}",
237247
r"\begin{tabular}{rlrrrrr}",r"\toprule",
238248
r"$N$ & Operator model & Test error & Density (\%) & Factor ($10^6$) & GB & ms \\",r"\midrule"]
239249
for n in NVALUES:
240-
for model in models:
250+
errs=[float(lookup[case,n,m]['test_error']) for m in models]
251+
errc=bold_best([fnum(v) for v in errs], errs)
252+
for model,ec in zip(models,errc):
241253
row=lookup[case,n,model]
242-
lines.append(f"{n} & {short[model]} & {fnum(row['test_error'])} & {100*float(row['kernel_density']):.2f} & {int(row['native_factor_nnz'])/1e6:.3f} & {float(row['accounted_resident_storage_ESTIMATE_decimal_GB']):.3f} & {1000*float(row['warm_100query_median_seconds']):.1f}"+r" \\")
254+
lines.append(f"{n} & {short[model]} & {ec} & {100*float(row['kernel_density']):.2f} & {int(row['native_factor_nnz'])/1e6:.3f} & {float(row['accounted_resident_storage_ESTIMATE_decimal_GB']):.3f} & {1000*float(row['warm_100query_median_seconds']):.1f}"+r" \\")
243255
if n!=10000: lines.append(r"\addlinespace[3pt]")
244256
lines += [r"\bottomrule",r"\end{tabular}",r"\end{table}", ""]
245257
path=SOURCE/"sections"/"gpu_all_models_tables.tex";path.write_text("\n".join(lines),encoding="utf-8");outputs.append(path)
246258

247259
lines=[r"\begin{table}[pos=tbp]",r"\centering\small",r"\setlength{\tabcolsep}{4pt}",
248-
r"\caption{Offline and initialization costs at $N=10000$. Selection includes kernel fitting, where applicable, and validation. Rebuild assembles and factors the selected operator. Initialization includes two synchronized 100-field prediction passes, compilation, and CUDA graph preparation. The four models share the setup of the output reconstruction and may reuse compiled code. The dash marks the selection time of the fixed-shape backward-facing-step model, which contains an external pause. Its raw time is in the released data.}",
260+
r"\caption{Offline and initialization costs at $N=10000$. Selection includes kernel fitting, where applicable, and validation. Rebuild assembles and factors the selected operator. Initialization includes two synchronized 100-field prediction passes, compilation, and CUDA graph preparation. The four models share the setup of the output reconstruction and may reuse compiled code. The dash marks the selection time of the fixed-shape backward-facing-step model, which contains an external pause. Its raw time is in the released data. Bold: smallest time in each column for each data set.}",
249261
r"\label{tab:gpu-offline-n10000}",r"\begin{tabular}{llrrr}",r"\toprule",
250262
r"Data set & Operator model & Selection (min) & Rebuild (s) & Initialization (s) \\",r"\midrule"]
251263
for case,title in CASES:
264+
rows=[lookup[case,10000,m] for m in models]
265+
ok=[r['selection_time_comparable_without_interruption']=='True' for r in rows]
266+
sel=[float(r['training_validation_minutes']) for r in rows]
267+
selv=[s for s,o in zip(sel,ok) if o]
268+
selc=[f"{s:.2f}" if o else "---" for s,o in zip(sel,ok)]
269+
best_sel=f"{min(selv):.2f}"
270+
selc=[bold(c) if c==best_sel else c for c in selc]
271+
reb=[float(r['operator_setup_seconds']) for r in rows]; rebc=bold_best([f"{v:.2f}" for v in reb], reb)
272+
ini=[float(r['first_prediction_compile_capture_seconds']) for r in rows]; inic=bold_best([f"{v:.2f}" for v in ini], ini)
252273
for i,model in enumerate(models):
253-
row=lookup[case,10000,model]
254-
selected=f"{float(row['training_validation_minutes']):.2f}" if row['selection_time_comparable_without_interruption']=='True' else "---"
255-
lines.append((title if i==0 else "")+" & "+short[model]+f" & {selected} & {float(row['operator_setup_seconds']):.2f} & {float(row['first_prediction_compile_capture_seconds']):.2f}"+r" \\")
274+
lines.append((title if i==0 else "")+" & "+short[model]+f" & {selc[i]} & {rebc[i]} & {inic[i]}"+r" \\")
256275
lines.append(r"\addlinespace[3pt]")
257276
lines += [r"\bottomrule",r"\end{tabular}",r"\end{table}"]
258277
path=SOURCE/"sections"/"gpu_offline_n10000.tex";path.write_text("\n".join(lines)+"\n",encoding="utf-8");outputs.append(path)
@@ -268,7 +287,7 @@ def decay_rate_table():
268287
xm = sum(x)/len(x)
269288
lines = [r"\begin{table}[pos=!htbp]", r"\centering\small",
270289
r"\setlength{\tabcolsep}{4pt}",
271-
r"\caption{Least-squares exponent $\tau$ of the test error $\propto N^{-\tau}$ over $N\in\{500,1000,5000,10000\}$.}",
290+
r"\caption{Least-squares exponent $\tau$ of the test error $\propto N^{-\tau}$ over $N\in\{500,1000,5000,10000\}$. Bold: fastest decay in each row.}",
272291
r"\label{tab:gpu-decay-rates}", r"\begin{tabular}{lrrrr}", r"\toprule",
273292
r"Data set & Fixed & Fitted & Fixed + affine & Fitted + affine \\", r"\midrule"]
274293
rates = {}
@@ -280,7 +299,7 @@ def decay_rate_table():
280299
slope = sum((a-xm)*(b-ym) for a, b in zip(x, y))/sum((a-xm)**2 for a in x)
281300
vals.append(-slope)
282301
rates[case] = vals
283-
lines.append(titles[case]+" & "+" & ".join(f"{v:.2f}".replace("-", "$-$") for v in vals)+r" \\")
302+
lines.append(titles[case]+" & "+" & ".join(bold_best([f"{v:.2f}".replace("-", "$-$") for v in vals], vals, best=max))+r" \\")
284303
lines.extend([r"\bottomrule", r"\end{tabular}", r"\end{table}"])
285304
path = SOURCE/"sections"/"gpu_decay_rates.tex"
286305
path.write_text("\n".join(lines)+"\n", encoding="utf-8")

0 commit comments

Comments
 (0)