Skip to content

feat: a solve page solves a scenario again in the browser, with its data and model open to edit, puts the answer beside the published one, and downloads it as an archive - #9

Merged
FBumann merged 5 commits into
mainfrom
feat/solve-in-browser
Oct 12, 2026
Merged

FBumann merged 5 commits into
mainfrom
feat/solve-in-browser

Conversation

@FBumann

@FBumann FBumann commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Prompt: Im wondering if we can make a fully fledged web app like the specsolve app, but include the solver in it through wasm.

Prompt: Add the table editor!

Prompt: Lets prepare a PR. Adjust for fluxopt/specsolve#1988

Prompt: Update the app as this merges and specsolve releases

Note

The following content was generated by AI.

A Solve page (#/solve) runs specsolve 0.9 and HiGHS in the tab, through Pyodide in a Web Worker. It takes a scenario's model and data from its published archive. You can edit both, solve, compare the answer with the published one, and download the run as a specsolve archive.

What the page does, and how

Page. Pick a scenario of runs/. The page fetches its spec.yaml, axis.json and every sources/<name>.parquet, and names the sources from the archive's sources.parquet. A static host cannot list a directory, so this list is necessary. It shows:

  • Data. Every parameter with a value column, in a TanStack Table with column filters. Type over a cell, or multiply the filtered rows by a factor. A changed cell is marked, and its tab counts the changes.
  • Model. The spec in a text editor.
  • Answer. Cost, periods optimal, solve time and wait time. A fleet chart for the solve in the tab, beside the published one. A per-period table of the cost difference. If specsolve refuses an edit, its message shows on the page.
  • Solve and download the archive. Solves the current inputs with archive= and saves the zip as <scenario>.zip, or <scenario>-edited.zip after an edit.

Worker (src/solver.worker.ts). Loads Pyodide from jsDelivr at the version package.json pins (pyodide@314.0.7, exact). Installs specsolve>=0.9,<0.10 with micropip, then runs src/solve.py. That file is about 40 lines and calls sps.solve_over(spec, tables, json.loads(axis), archive=…). Since fluxopt/specsolve#1981, the archive's axis data goes in as it is, carry included.

Parquet in the browser. Pyodide's polars has no parquet. The page reads the sources with hyparquet, which it already used, and passes them to Python as {column: values}. The answer comes back the same way: the record, and every variable's values. Pyodide converts a whole-number JS value to a Python int, so the tables are built with strict=False. To write an archive, specsolve uses pyarrow where polars has no parquet (fluxopt/specsolve#1991). So the worker loads pyarrow the first time a download is asked for, not on every visit.

Shared code. archive.ts exports rowsOf, so a parquet file already in memory reads like a published one. vite.config.ts keeps pyodide out of dependency pre-bundling and builds the worker as an ES module.

What was verified, and how

All in headless Chromium against a local build of this branch at 6abdbc0. specsolve was 0.9.0 from PyPI, installed by micropip in Pyodide, as the live page will install it. This container cannot reach jsDelivr or fluxopt.github.io, so:

  • The Pyodide 314.0.7 release tarball served the CDN requests.
  • The archives were a local publish of the showcase's four scenarios, written with specsolve 0.6.2, as the live ones are.
check result
base and carbon_cap, each period optimal; same cost as published, within 1e-15 relative
base with solar's invest × 0.6, through the editor the published cheap_solar cost in all four periods
two single-cell edits, then undo both kept, then both reverted
a spec edited to dtype: integer specsolve's SchemaError shown on the page
that edited base, downloaded base-edited.zip, 30 members, 55 KB
the zip, natively (specsolve 0.9.0 from PyPI, polars 2.0.0) load_archive reads it; objectives = published cheap_solar within 6.3e-16; load_inputs → solve_over gives the same objectives
first solve, from the click 15–20 s, almost all of it loading Python; 37.9 MB from the CDN
solve alone, then a second solve 1.1–1.4 s, then 0.4 s (build and solve of four periods, in the worker)
first download, from the click 6–8 s, almost all of it loading pyarrow; 14.9 MB more from the CDN
a second download 0.6 s
renderer memory about 780 MB after two solves; about 1.1 GB once pyarrow is loaded
390 px wide usable; the period table scrolls inside its card

npm run build passes, here and in CI.

Not done
  • Explore cannot open a run solved in the tab. The download is a real archive, but Explore reads stacked files from a published directory.
  • Only the four runs/ scenarios. The worker takes any sweep archive, but the grid and the hedge are not offered.
  • Not tested against live hosts. The real jsDelivr CDN and the live archives on fluxopt.github.io were not reachable from here.
  • The console shows a polars panic once per worker. specsolve's streaming probe triggers it, and catches it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AAz2z9GVsHSQypiTUZyrY9

…with the model open to edit, and puts the answer beside the published one

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AAz2z9GVsHSQypiTUZyrY9
…a time or a filtered set of rows at once

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AAz2z9GVsHSQypiTUZyrY9
…r's polars, and passes it the archive's axis as written

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AAz2z9GVsHSQypiTUZyrY9
…through pyarrow in the browser

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AAz2z9GVsHSQypiTUZyrY9
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AAz2z9GVsHSQypiTUZyrY9
@FBumann FBumann changed the title feat: a solve page solves a scenario again in the browser, with its data and model open to edit, and puts the answer beside the published one feat: a solve page solves a scenario again in the browser, with its data and model open to edit, puts the answer beside the published one, and downloads it as an archive Oct 12, 2026
@FBumann
FBumann marked this pull request as ready for review October 12, 2026 09:38
@FBumann
FBumann merged commit 5570c73 into main Oct 12, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants