Commit Graph

56 Commits

Author SHA1 Message Date
Michael Pilosov
a4fc36352d compare: axes dropdown combines sync (scaled/locked) × aspect (1:1/3:2)
Canvas height now derives from column width via aspect-ratio (CSS custom
prop --canvas-aspect set by JS on the grid host), with --panel-h as a
ceiling. Dropdown options: scaled/locked × 1:1/3:2. Default scaled 3:2.
Legacy 'independent'/'locked' values still parse. Canvas resizes after
aspect changes via requestAnimationFrame.
2026-04-22 16:57:15 -06:00
Michael Pilosov
9b178dad38 runs: filter chips + compare selection up to 8
- /compare accepts ?stem=…&stem=… (repeated) for 2-8 runs; legacy ?a=&b=
  still works. compare.js parses multi-stem; template drops stem_a/_b
  data attrs that were unused.
- compare-select.js: MAX bumped to 8, button enables at 2-8 selected.
  URL emitted as ?stem=… per selection.
- runs list gets a dataset/algorithm chip filter bar above #runs-slot
  (pattern ported from metrics.js). Chips reflect the union of values in
  the current list; selection state persists across htmx swaps. Non-
  matching rows get .filtered-out (display:none).
- _runs.html li now carries data-embedder/data-generator so the filter
  can key on them.
2026-04-22 16:41:06 -06:00
Michael Pilosov
b744c48348 stems: fold generator_kwargs into the hash; fix swiss_roll vs hole ambiguity
- run_args_hash now covers (embed_args, generator_kwargs). When gen_kwargs
  is empty we still hash embed_args alone — so plain generators (s_curve,
  plain swiss_roll) keep their stems and no existing plain-gen figs need
  renaming. Kwargs-bearing variants (swiss_roll_hole, blobs,
  gaussian_quantiles, classification) now disambiguate properly.
- Flow persists generator_kwargs into metrics.json meta AND into the
  frames.json sidecar meta, so the label-enrichment path can find it
  without another lookup.
- _enrich_with_labels discovers gen_kwargs in priority: payload meta -->
  sibling metrics.json --> DATASET_META first-match. It matches the
  DATASET_META entry by (path, kwargs) so swiss_roll_hole is no longer
  confused for plain swiss_roll.
- _cached_frames overrides meta.stem with the URL-requested stem before
  enrichment — after a backfill rename the sidecar's baked-in stem is
  stale, and we were then failing to find the sibling metrics.json.
- Submit duplicate-check uses the new hash and keeps the hashless-legacy
  check as a safety net.
- backfill_hashes.py rewritten: queries Prefect for each recent run's
  full params, finds the matching fig under any of (current, legacy,
  hashless) names, renames to the current scheme and patches
  generator_kwargs into metrics.json.
2026-04-22 16:30:42 -06:00
Michael Pilosov
44de8deeeb viz: extract N-panel-agnostic module; homepage modal reuses it for single-run view
- panel-grid.js (new): exports mountPanels({host, controls, stems}) → {destroy}.
  Moved createPanel + shared control wiring + linked-hover + pad-to-match
  time mapping out of compare.js. Stem-count-agnostic; works for 1, 2, or N.
- Panel DOM is cloned from <template id=compare-panel-tpl> on each page.
- compare.js is now a ~10-line shim: parse ?a=&b=, call mountPanels.
- Per-panel color is viridis-sampled by index/N (middle viridis for N=1,
  ends-of-palette for N=2, linear lerp for N≤8, cycle at N≥9). Set as
  --panel-color on the panel element; CSS reads it for tag/time-seg.
- Homepage <dialog id=run-modal> + run-modal.js hijack the 'embedding' link
  (plain click → modal; meta/ctrl/middle-click still opens plotly HTML).
  Dialog close disposes every panel's renderer/geometry/material.
- .compare-grid → repeat(auto-fit, minmax(360px, 1fr)) handles N=1..many,
  replaces the <900px one-column media rule.
- Runs list: relabel Prefect's 'Late' state as 'Queued' — more honest
  description of what the runner is doing at the concurrency cap.
2026-04-22 16:17:01 -06:00
Michael Pilosov
fe49565651 stems: include embed_args hash in output filename + emit frames.json sidecar
Stem grows an 8-hex sha1 digest of the (keys-sorted) embed_args dict, so
runs differing only in embed_args (e.g. UMAP n_neighbors=5 vs 15) now
produce distinct figs. The stem regex and parser both accept an optional
_<hash> tail so pre-hash figs still render in the runs list and compare
page; legacy filename is resolved on disk fallback.

Duplicate-submission check now rejects against BOTH the hashed and the
legacy hashless variant so users can't accidentally duplicate an old run
either.

Flow additionally writes a <stem>.frames.json sidecar next to the plotly
HTML (same shape as app/web/plotly_parse returns). Server prefers the
sidecar when present; falls back to parsing HTML for older runs. Sidecar
emission is non-critical — any failure just logs and keeps going.
2026-04-22 15:52:39 -06:00
Michael Pilosov
36e217f51e submit: reject runs whose output would overwrite an existing fig
Before dispatching to Prefect, check if figs/<stem>.html already exists.
If so, return 409 with a message asking to bump seed/jitter/N/T or delete
the fig. Stem is derived from (generator, embedder, N, T, J, seed), so
embed_args-only changes still collide and are blocked — users must vary
a stem-shaping param to get a distinct output file.
2026-04-22 15:39:05 -06:00
Michael Pilosov
a5614ac371 runs list: mark older runs with duplicate-stem output as stale, hide their compare checkbox
When two runs share identical params they write to the same figs/<stem>.html,
and the most recent overwrites the earlier. Previously both got a compare
checkbox with the same data-stem, so toggling one toggled both via the JS
Set. Now we flag older duplicates server-side (first occurrence wins — Prefect
returns runs START_TIME_DESC), drop their checkbox, fade the row, and mark
'overwritten' on the outputs line.
2026-04-22 15:35:05 -06:00
Michael Pilosov
fc1ae9dbc9 compare: default color mode to 'original' 2026-04-22 15:30:50 -06:00
Michael Pilosov
9277229024 compare: color points by their original-dataset label (mono|original toggle)
Server enrichment regenerates the dataset deterministically (random_state=0,
matching the flow's _DEFAULT_GENERATOR_KWARGS — the stem's seed drives jitter,
not generation) and attaches per-point labels + label_kind to frames.json.

Client picks the dataset-picker's scheme: continuous ramp for s_curve/swiss_roll,
8-color categorical palette for blobs/gaussian_quantiles/classification. Jitter-
added points (id >= num_points) render black. Rainbow material is opaque with
alpha cutoff so overlapping points don't blend to the ramp midpoint.

Swiss_roll and swiss_roll_hole collide on generator_path; the plain variant
wins for now (kwargs aren't preserved through the flow's metrics.json).

Bumped Cache-Control on the frames endpoint so browsers don't cache stale
pre-enrichment payloads.
2026-04-22 15:29:03 -06:00
Michael Pilosov
d3f5088233 compare: pad-to-match time mapping + fix stalled play at small du
- applyU now maps u to a shared global frame index uGlobal in [0, maxT-1];
  each panel clamps to its own (T-1), so a shorter timeline pads its last
  frame while the longer one finishes — both advance at the same wall-clock
  tempo instead of rescaling their timelines.
- tick() keeps u as a float closure variable; reading it back from the
  integer-step scrubber was quantizing du to 0 at slow tempo + high T
  (1600ms/frame, T=24: du ≈ 4e-4 → round to 0 on scrub), stalling playback
  after one frame.
2026-04-22 14:44:08 -06:00
Michael Pilosov
89401e3aee compare: extend mismatch highlighting to embedder and generator slugs 2026-04-22 14:38:36 -06:00
Michael Pilosov
41ce5ee88a compare: highlight mismatched N/T/J/s params in red between the two panels 2026-04-22 14:37:04 -06:00
Michael Pilosov
c9868ff83e compare: wrap N/T/J/seed params onto its own line in each panel header 2026-04-22 14:35:24 -06:00
Michael Pilosov
dd01638110 compare: replace stem-link filename text with a new-tab arrow glyph 2026-04-22 14:33:18 -06:00
Michael Pilosov
8bc8b801dc compare: move control bar above the panels 2026-04-22 14:32:32 -06:00
Michael Pilosov
a976ba893a compare: slow base playback 4x (1x = 1600ms/frame, was 400ms) 2026-04-22 14:31:20 -06:00
Michael Pilosov
d0b026734a compare: interpolate between frames for smooth point-trajectory motion
Default is smooth (lerp each point between adjacent frames); a motion:step
toggle preserves the snap-to-frame behaviour. Points missing in either
adjacent frame are hidden during the transition to avoid pop-in.
2026-04-22 14:29:24 -06:00
Michael Pilosov
fc6aad5516 compare: side-by-side three.js animation with linked scrub, hover, and theme
- /compare page renders two ortho-camera panels fed by /api/runs/{stem}/frames.json
- shared controls: play/pause, scrubber, speed (0.5-4x), axes sync (independent/locked)
- linked hover: picks nearest point in one panel, highlights matching point_id in other
- add/remove-jitter nulls handled via per-frame packed positions + setDrawRange
- independent error states per panel; theme-aware colors via themechange event
2026-04-22 14:26:45 -06:00
Michael Pilosov
e680867f8b compare: selection UX on runs list + /compare placeholder page
- per-run checkbox when embedding HTML exists; cap at 2 selected
- sticky 'compare selected' button opens /compare?a=&b= in new tab
- selection state persists across the 3s htmx poll via a Set keyed by stem
- /compare stub validates stems, renders scaffolding (three.js UI next)
2026-04-22 14:19:26 -06:00
Michael Pilosov
b016dbdaee compare: parse plotly HTML into frames JSON, expose at /api/runs/{stem}/frames.json 2026-04-22 14:16:30 -06:00
Michael Pilosov
acb596743a reducer list: right-align pkg column so wrapped class-path lines stay on the right 2026-04-22 13:58:01 -06:00
Michael Pilosov
4ee78dd466 reducer list: break long class paths on '.' so they wrap cleanly on narrow (non-mobile) viewports 2026-04-22 11:48:52 -06:00
Michael Pilosov
958fa019ea reducers: add KernelPCA, Isomap, MDS, SpectralEmbedding, GaussianRandomProjection 2026-04-22 11:45:02 -06:00
Michael Pilosov
97ee3d4db6 reducer list: blurb spans full width on narrow viewports 2026-04-22 11:34:24 -06:00
Michael Pilosov
f524dcce51 clean up / clarify labels 2026-04-22 11:30:52 -06:00
Michael Pilosov
b108204289 fix min height 2026-04-22 11:25:30 -06:00
Michael Pilosov
8db92089a1 theme toggle: absolute (not fixed) so it doesn't follow scroll 2026-04-22 11:16:20 -06:00
Michael Pilosov
f07fa567d0 anchor theme toggle to top-right of viewport 2026-04-22 11:14:16 -06:00
Michael Pilosov
7039b39d9d nowrap nav link and accordion toggles on small viewports 2026-04-22 11:13:03 -06:00
Michael Pilosov
2e76cda332 page titles: drop web1 and em-dashes 2026-04-22 11:10:41 -06:00
Michael Pilosov
03309e28a6 move prefect badge from masthead to footer 2026-04-22 11:09:44 -06:00
Michael Pilosov
8bb186269b header: 'deployment' -> 'ver' 2026-04-22 11:08:14 -06:00
Michael Pilosov
5ce69f2ef0 inline metrics into home page; footer copyright; drop prefect-api URL
Migrates the metrics view from a separate /metrics page into an inline
§ 5 collapsible section on the home page (swept in from pre-existing
unstaged edits). Also updates the footer to © 2026 Mind the Math LLC
and drops the prefect-api URL from both mastheads.
2026-04-22 11:08:14 -06:00
Michael Pilosov
86e11a6a11 restyle sec 0 + incl fig 2026-04-22 10:57:24 -06:00
Michael Pilosov
bb46e5a18d add §0 introduction (default collapsed)
Three short paragraphs framing what the notebook studies: stability of
2-D embeddings under controlled perturbation of the input over time,
the two metrics logged per run, and why the streaming/longitudinal
angle matters for both visualization and downstream classification.
2026-04-22 10:41:24 -06:00
Michael Pilosov
1807b3ac4e add pacmap.LocalMAP to the reducer catalogue 2026-04-21 22:13:37 -06:00
Michael Pilosov
158f3fdefa theme picker canvas backgrounds
Read --picker-panel from the card's computed style at scene creation
and re-apply on themechange, so the three-js canvases flip with the
rest of the UI instead of staying stuck on the light palette.
2026-04-21 21:56:14 -06:00
Michael Pilosov
d385337a36 require dataset selection before submit
Client-side: disable the form submit button until the picker writes a
dataset_id. Server-side: reject posts where dataset_id is present but
empty, instead of silently defaulting to make_s_curve.
2026-04-21 21:54:50 -06:00
Michael Pilosov
61e9221b3a dark/light theme toggle 2026-04-21 21:51:16 -06:00
Michael Pilosov
ca59516f26 multiselect chips for dataset/algo filters 2026-04-21 21:07:33 -06:00
Michael Pilosov
b2be3d0835 metrics page with svg plots and filters 2026-04-21 21:02:58 -06:00
Michael Pilosov
c6bd693058 runtime display 2026-04-21 20:22:38 -06:00
Michael Pilosov
b99406671e simplify comment in UI re: locked 2D 2026-04-21 20:16:58 -06:00
Michael Pilosov
b99a8e3287 num snapshots fallback in frontend recent runs 2026-04-21 20:08:10 -06:00
Michael Pilosov
eaeb6b5c2a styling changes (radio for N) 2026-04-21 20:07:42 -06:00
Michael Pilosov
92069a3c91 rename snapshots -> timesteps 2026-04-21 19:55:01 -06:00
Michael Pilosov
afc9b5b2f2 migrate new data picker 2026-04-21 19:46:31 -06:00
Michael Pilosov
ca0ad9fd2d rename folder 2026-04-21 19:30:45 -06:00
Michael Pilosov
ac511c942f adding in more examples 2026-04-21 19:14:23 -06:00
Michael Pilosov
22ca411210 web1 checkpoint 2026-04-21 19:04:23 -06:00