-
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathTaskfile.yml
More file actions
321 lines (291 loc) · 14.5 KB
/
Copy pathTaskfile.yml
File metadata and controls
321 lines (291 loc) · 14.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
version: "3"
# Top-level recipes for basin maintainer workflows.
# Run `task --list` to see available recipes.
tasks:
lint:
desc: "Lint the Rust codebase (clippy) and the web app (eslint)."
cmds:
- cargo clippy --workspace --all-targets --all-features -- -D warnings
- cd web && pnpm check
logo:
desc: "Watch the logo example; rebuild day + dark SVG/PNGs and sync to web/static on save."
summary: |
Renders crates/basin/examples/logo.rs to images/logo.svg (day theme) and
images/logo-dark.svg (night theme), rasterises PNGs at a few widths
(resvg), and copies the SVGs + PNGs into web/static/ for the web app —
then watches the example and rebuilds on every save. The example traces a
real GradientDescent run over its surface, so the river is genuine
optimizer output.
The night theme re-tints the day palette through an Oklab "moonlight"
transform and swaps the props (sun → full moon + stars, butterfly → owl);
both themes render at the same frame size so they swap cleanly under a
prefers-color-scheme / dark-mode toggle.
Watch mode by default (watch: true) — leave `task logo` running while you
tweak constants; Ctrl-C to stop. For a single build (CI, committing the
assets) use `task logo:once`.
Outputs (in images/ and web/static/): logo{,-dark}.svg (scalable,
transparent), logo{,-dark}.png (1024px wide), logo{,-dark}-512.png,
logo{,-dark}-256.png. Aspect ratio is preserved; PNG rasterisation uses
`resvg` (pinned in devenv.nix).
watch: true
sources:
- crates/basin/examples/logo.rs
cmds:
- task: logo:build
logo:once:
desc: "Build the logo once: day + dark SVG/PNGs, synced to web/static (no watch)."
sources:
- crates/basin/examples/logo.rs
generates:
- images/logo.svg
- images/logo.png
- images/logo-512.png
- images/logo-256.png
- images/logo-dark.svg
- images/logo-dark.png
- images/logo-dark-512.png
- images/logo-dark-256.png
- web/static/logo.svg
- web/static/logo.png
- web/static/logo-dark.svg
- web/static/logo-dark.png
cmds:
- task: logo:build
logo:build:
internal: true
cmds:
# 1. render the scalable, transparent master SVGs (day + night themes)
- cargo run --quiet --package basin --example logo -- images/logo.svg
- cargo run --quiet --package basin --example logo -- images/logo-dark.svg dark
# 2. rasterise PNGs at a few widths (aspect preserved, transparent)
- resvg images/logo.svg images/logo.png --width 1024
- resvg images/logo.svg images/logo-512.png --width 512
- resvg images/logo.svg images/logo-256.png --width 256
- resvg images/logo-dark.svg images/logo-dark.png --width 1024
- resvg images/logo-dark.svg images/logo-dark-512.png --width 512
- resvg images/logo-dark.svg images/logo-dark-256.png --width 256
# 3. sync the asset set into the web app's static dir
- cp images/logo.svg images/logo.png images/logo-512.png images/logo-256.png web/static/
- cp images/logo-dark.svg images/logo-dark.png images/logo-dark-512.png images/logo-dark-256.png web/static/
favicon:
desc: "Watch the favicon example; rebuild the self-adapting SVG and the PNG/ICO set and sync to web/static on save."
summary: |
Renders crates/basin/examples/favicon.rs to images/favicon.svg — a flat,
side-on cross-section of a 1D basin, drawn to read at 16-32 px in a browser
tab. The rivulet feeding the pool is a genuine GradientDescent run over the
curve, like the logo's river. The SVG is self-adapting: it fuses the day
palette and an Oklab "moonlight" night re-tint into one file (#light-icon /
#dark-icon groups switched by prefers-color-scheme), so a single
<link rel="icon"> covers light and dark tab chrome.
From a day-only render it rasterises the favicon set the site references
(resvg + ImageMagick): favicon-96x96.png and a 16/32/48 favicon.ico for
browser tabs, plus apple-touch-icon.png and the maskable
web-app-manifest-{192,512}.png on a brand-cream tile (with safe-zone
padding for Android's icon mask). Everything is copied into web/static/.
Watch mode by default; Ctrl-C to stop. For a single build (CI, committing
the assets) use `task favicon:once`.
watch: true
sources:
- crates/basin/examples/favicon.rs
cmds:
- task: favicon:build
favicon:once:
desc: "Build the favicon once: self-adapting SVG and the PNG/ICO raster set, synced to web/static (no watch)."
sources:
- crates/basin/examples/favicon.rs
generates:
- images/favicon.svg
- images/favicon-96x96.png
- images/favicon.ico
- images/apple-touch-icon.png
- images/web-app-manifest-192x192.png
- images/web-app-manifest-512x512.png
- web/static/favicon.svg
- web/static/favicon-96x96.png
- web/static/favicon.ico
- web/static/apple-touch-icon.png
- web/static/web-app-manifest-192x192.png
- web/static/web-app-manifest-512x512.png
cmds:
- task: favicon:build
favicon:build:
internal: true
cmds:
# 1. master SVGs: one self-adapting favicon.svg (light + dark fused into a
# single file, switched by prefers-color-scheme) shipped as the SVG
# favicon, plus a day-only render used purely as the raster source —
# .ico / PNG fallbacks can't adapt, and resvg only honours the default
# branch of the media query anyway.
- cargo run --quiet --package basin --example favicon -- images/favicon.svg adaptive
- cargo run --quiet --package basin --example favicon -- images/favicon-raster.svg
# 2. browser-tab rasters from the day SVG (transparent): a 96px PNG and a
# multi-resolution .ico. Sizes 16/32/48 only — RealFaviconGenerator's
# set; a 64px entry trips its checker as an "extra size".
- resvg images/favicon-raster.svg images/favicon-96x96.png --width 96
- resvg images/favicon-raster.svg images/favicon-256.png --width 256
- magick images/favicon-256.png -define icon:auto-resize=48,32,16 images/favicon.ico
- rm -f images/favicon-256.png
# 3. app icons on a fully opaque brand-cream (#f4f1de) tile. apple-touch:
# full-bleed flatten. Maskable manifest icons: the basin is a WIDE
# isometric diamond (~97% of the frame width), so it's rendered at ~64%
# and *composited* onto a solid cream tile (xc: + -composite, not
# -extent — -extent leaves the interior transparent, so Android masks
# the bare art and clips the diamond's tips). 64% keeps the tips well
# inside the maskable safe zone (central 80% circle).
- resvg images/favicon-raster.svg images/favicon-180.png --width 180
- magick images/favicon-180.png -background "#f4f1de" -flatten images/apple-touch-icon.png
- rm -f images/favicon-180.png
- resvg images/favicon-raster.svg images/favicon-m192.png --width 124
- magick -size 192x192 xc:"#f4f1de" images/favicon-m192.png -gravity center -composite images/web-app-manifest-192x192.png
- rm -f images/favicon-m192.png
- resvg images/favicon-raster.svg images/favicon-m512.png --width 330
- magick -size 512x512 xc:"#f4f1de" images/favicon-m512.png -gravity center -composite images/web-app-manifest-512x512.png
- rm -f images/favicon-m512.png
- rm -f images/favicon-raster.svg
# 4. sync the favicon set into the web app's static dir (one self-adapting
# SVG — no separate favicon-dark.svg)
- cp images/favicon.svg images/favicon-96x96.png images/favicon.ico images/apple-touch-icon.png images/web-app-manifest-192x192.png images/web-app-manifest-512x512.png web/static/
ingest-paper:
desc: "Fast first-pass parse of a PDF paper into references/<NAME>/source.md (pymupdf4llm)."
summary: |
Args:
PDF=path/to/paper.pdf source PDF (copied to references/<NAME>/source.pdf)
NAME=lbfgs slug for the references/<NAME>/ directory
Output:
references/<NAME>/source.pdf copy of the input
references/<NAME>/source.md pymupdf4llm-parsed markdown (full doc)
pymupdf4llm is fast (~1s) but mangles equations and drops figures. For
pages where math fidelity matters, follow up with `task ingest-paper-pages`.
requires:
vars: [PDF, NAME]
cmds:
- mkdir -p references/{{.NAME}}
- cp -n {{.PDF}} references/{{.NAME}}/source.pdf
- >
uv run --project tools python -c
"import pymupdf4llm, pathlib;
p = pathlib.Path('references/{{.NAME}}');
p.joinpath('source.md').write_text(pymupdf4llm.to_markdown(str(p / 'source.pdf')))"
ingest-paper-pages:
desc: "Marker pass on selected PDF pages (slow, high quality math/figures). Outputs source.marker.md."
summary: |
Args:
NAME=lbfgs slug under references/
PAGES="3-4,7" 0-indexed PDF page range (marker convention)
Output:
references/<NAME>/source.marker.md marker-parsed markdown for those pages
Marker is ML-based and slow (~minutes per page). Run only on pages where
pymupdf4llm's output is unusable (dense equations, critical figures).
Pages are processed ONE AT A TIME in separate marker processes, then
concatenated, so memory is released between pages. This is required on the
16 GB AMD card: surya's recognition encoder accumulates ~3 GB/page and
ROCm's scaled_dot_product_attention falls back to the math backend (no
flash/mem-efficient kernel), so a multi-page process OOMs. Per-page keeps
peak VRAM bounded regardless of how many pages are requested.
requires:
vars: [NAME, PAGES]
cmds:
# LD_LIBRARY_PATH (scoped to this command) lets the ROCm torch wheel find
# its base system libs so marker runs on the AMD GPU; TORCH_ROCM_LIB_PATH is
# set by devenv.nix (empty/harmless elsewhere). The batch caps +
# expandable_segments keep surya's models within the 16 GB card.
- |
set -eu
# Expand PAGES ("3", "7-31", "3,7-8") into a 0-indexed page list.
pages=""
for tok in $(echo "{{.PAGES}}" | tr ',' ' '); do
case "$tok" in
*-*) for p in $(seq "${tok%-*}" "${tok#*-}"); do pages="$pages $p"; done ;;
*) pages="$pages $tok" ;;
esac
done
work=$(mktemp -d)
: > references/{{.NAME}}/source.marker.md
for p in $pages; do
echo ">>> marker page $p"
LD_LIBRARY_PATH="${TORCH_ROCM_LIB_PATH:-}" \
PYTORCH_ALLOC_CONF=expandable_segments:True \
RECOGNITION_BATCH_SIZE=8 DETECTOR_BATCH_SIZE=1 LAYOUT_BATCH_SIZE=1 \
OCR_ERROR_BATCH_SIZE=8 TABLE_REC_BATCH_SIZE=1 \
uv run --project tools marker_single references/{{.NAME}}/source.pdf \
--config_json tools/marker_config.json \
--page_range "$p" --output_dir "$work/$p/"
cat "$work/$p/source/source.md" >> references/{{.NAME}}/source.marker.md
printf '\n' >> references/{{.NAME}}/source.marker.md
done
rm -rf "$work"
web-dev:
desc: Start Svelte development server
dir: web
cmds:
- pnpm dev
bench:backends:
desc: "Run the backend-axis benchmark and refresh the /benchmarks page data."
summary: |
Runs the `solver_backends` criterion bench — GD / Nelder-Mead /
unconstrained L-BFGS on Rosenbrock across all four linear-algebra
backends (Vec, nalgebra, ndarray, faer) — then parses criterion's output
into web/src/lib/data/backend-benchmarks.json, which the /benchmarks page
imports.
Timing-sensitive: run on an otherwise-idle machine and commit the
regenerated JSON. Deliberately not wired into CI — shared runners are too
noisy for trustworthy microbenchmarks.
cmds:
- cargo bench --features nalgebra,ndarray,faer --bench solver_backends
- cd web && pnpm collect:benchmarks
bench:competitors:
desc: "Run the competitor-axis trace harness and refresh the /benchmarks/competitors data."
summary: |
Runs the `trace` harness — basin vs argmin on GD / Nelder-Mead / L-BFGS
over Rosenbrock, recording suboptimality against wall-clock time at every
iteration — then wraps the traces with run metadata into
web/src/lib/data/competitor-benchmarks.json, which the
/benchmarks/competitors page imports.
Timing-sensitive: run on an otherwise-idle machine and commit the
regenerated JSON. Deliberately not wired into CI — shared runners are too
noisy for trustworthy timings.
cmds:
- cargo run -p competitor-bench --release --bin trace > target/competitor-traces.json
- cd web && pnpm collect:competitors
bench:solvers:
desc: "Run the solver-axis comparison and refresh the /benchmarks/solvers data."
summary: |
Runs the `solver_compare` harness — basin's five general optimizers
(GD / Nelder-Mead / BFGS / L-BFGS / CMA-ES) on Rosenbrock n=2 from six
curated starting points, each capped on a 20 ms wall-clock budget and
stopped early on reaching suboptimality 1e-10. Traces are downsampled to
a log-spaced time grid in the harness, then wrapped with run metadata
into web/src/lib/data/solver-benchmarks.json, which the
/benchmarks/solvers page imports.
Timing-sensitive: run on an otherwise-idle machine and commit the
regenerated JSON. Deliberately not wired into CI — shared runners are too
noisy for trustworthy timings.
cmds:
- cargo run -p competitor-bench --release --bin solver_compare > target/solver-traces.json
- cd web && pnpm collect:solvers
paper:build:
desc: "Build the JOSS paper (paper/paper.pdf) with the Open Journals inara image."
summary: |
Runs the openjournals/inara Docker image over paper/ with JOURNAL=joss,
producing paper/paper.pdf from paper/paper.md and paper/paper.bib. Needs
Docker and network access (to pull the image on first run).
cmds:
- >
docker run --rm
--volume $PWD/paper:/data
--user $(id -u):$(id -g)
--env JOURNAL=joss
openjournals/inara
paper:preview:
desc: "Rebuild the paper on save and (re)open it in zathura."
watch: true
sources:
- paper/paper.md
- paper/paper.bib
cmds:
- task: paper:build
- pgrep -f "zathura.*paper/paper.pdf" || (zathura paper/paper.pdf &)
paper:wordcount:
desc: "Count words in the paper body (pandoc plain-text render)."
cmds:
- pandoc paper/paper.md -t plain | wc -w