Repository navigation
WASM: browser support for QuantEcon.py (Numba + JupyterLite) — tracking issue #925
Description
Note
Updated 2026-09-09. Revised after an adversarial re-check of every claim in this body against live state. Since 2026-08-31: PR #937 merged, so #926 is closed and Gap 1 is resolved; PR #947 removed the sympy dependency; #950 (WASM testbed site) was opened and linked as a sub-issue on 09-07 and is now carried in the plan; and item 1 of #931 was falsified — network calls work in the browser (@oyamad, 2026-09-06), so PR #948 needs rework. Two claims in the previous revision are corrected below (coverage is not packaged for WASM; the dependency list). Every row in the Gaps and Work plan tables now carries an evidence marker. The revision log is in the comments.
Evidence convention
The failure in #931 was a claim about browser behaviour written from first principles rather than from a run, and it reached a merge-ready PR before anyone tested it. To make that class of error visible, every claim below carries one of three markers. Add the marker whenever you add a claim, and cite the run or the file.
| Marker | Meaning |
|---|---|
| [browser] | Observed in a real xeus-python kernel; the comment or run is linked |
| [source] | Verified by reading the code, a patch, or a recipe in this repo or upstream |
| [inferred] | Reasoned from platform properties, not observed. Treat as a hypothesis, and do not build a PR on it without a browser check |
Background
On 11 August 2026 QuantStack announced Numba in the Browser (Anutosh Bhat): a genuine Numba JIT running entirely in the browser. Python functions are compiled through llvmlite to WebAssembly, linked in-process with LLD, and loaded as Emscripten side modules into a JupyterLite kernel — no server anywhere. Numba is now published on emscripten-forge (numba 0.67.0 build 1 + 10 emscripten patches; llvmlite 0.49.0 + 4 patches), and the announcement showcases the economics stack built on top of it (EconForge's interpolation.py and Dolo.py, PyMC/PyTensor). QuantEcon.py is the obvious next citizen of that ecosystem, and its authors explicitly invite the wider Numba ecosystem to validate their packages.
Concretely, "browser support" for QuantEcon.py means: works in JupyterLite with the xeus-python kernel against the emscripten-forge + conda-forge channels. This is not the classic Pyodide kernel — Numba remains unavailable there (see numba/numba#3284, pyodide/pyodide-recipes#192).
Where we stand (verified 2026-10-05)
Re-verified 2026-10-05: the project stays active, and two items wait on upstream. Every row of the table below was re-checked against live state; rows that changed are re-dated, and the others stand as written on 2026-09-09. What moved: the import-time compilation fix landed (PR #957, merged 2026-09-23, @oyamad's implementation) and shipped in v0.12.0 on 2026-09-24, so the four gufuncs in Gap 3 no longer compile during import quantecon; #930 stays open because the browser import time is still unmeasured, and the Gap 3 and #930 rows further down predate this. What has not: the testbed site (#950), the lead item, has not started, and no browser run has yet come from this repository (the WASM smoke suite workflow runs the suite natively; its browser job is #933). The two upstream waits: #944 waits on emscripten-forge/recipes#6309, which has had no maintainer reply since 2026-08-20; #927 waits on an upstream fix for the jitted-generator hang, and the report that would ask for it is still a draft in the #927 thread, so the first move there is ours. Everything else is unblocked: #950 first, then the browser checks that #929 (PR #952) and #930 are held for.
| Fact | Value | Evidence |
|---|---|---|
main |
bcc18b5; twelve commits since the last revision, one of them WASM work (#957, as 79360d6). Released as v0.12.0 on 2026-09-24 | [source] GitHub compare 0c7c816...main, 2026-10-05 |
| Sub-issues | 11 linked, 1 closed (#926 via PR #937); unchanged since 2026-09-09 | [source] GitHub, 2026-10-05 |
| Open PRs | #948 (#931, now conflicts with main, needs rework — its premise is falsified); #952 (#929, CLEAN, premise unverified in browser) |
[source] GitHub, 2026-10-05 |
| Closed PRs | #957 merged 2026-09-23 (part of #930, which stays open); #943 closed unmerged 2026-09-10, replaced by #957; #937 merged 2026-09-09 (closed #926); #938 merged; #935, #942 closed unmerged | [source] GitHub, 2026-10-05 |
| Runtime dependencies | Four: numba, numpy, requests, scipy. sympy removed by PR #947 (2026-09-09) | [source] pyproject.toml @ bcc18b5 |
| Branches in this repo | wasm-930-dynamic-gufuncs (7c512ed, oyamad, 2026-08-23); its implementation landed as PR #957, so the branch can go |
[source] GitHub, 2026-10-05 |
| emscripten-forge numba | 0.67.0 build 1, 10 patches; recipe unchanged since 9a2db20 (2026-08-18), re-checked 2026-10-05 | [source] recipe history, 2026-10-05 |
| emscripten-forge/recipes#6309 (#944) | open; no maintainer reply since oyamad's repro on 2026-08-20 | [source] GitHub, 2026-10-05 |
| Jitted-generator hang (#927) | root cause confirmed by symbol probe and a monkeypatch that makes the generator return; still no upstream report filed | [browser] #927 thread, 2026-08-19; no upstream report found on 2026-10-05 [source] |
| numba/numba#10128 | open; the candidate fix at numba/numba#10545 is open and unmerged | [source] GitHub, 2026-10-05 |
| Browser runs from this repo's CI | still none. All browser evidence comes from the public numba-ecosystem demo, run by hand | [source] #928 results table |
| #928 evidence table | 5 of 14 rows have browser evidence; 9 remain "not yet run" | [source] #928, 2026-09-09 |
The starting position is strong. QuantEcon.py is pure Python (noarch on conda-forge, universal wheel), every runtime dependency is already resolvable for the browser, and all ~90 jitted callables are nopython-mode with lazy specialisation — exactly the model the WASM engine implements. import quantecon already works in the xeus-python kernel, _numba_linalg_solve / np.linalg.solve inside @njit return correct results there, and the requests / urllib network paths work too [browser].
The library's widespread cache=True discipline should become an asset under JupyterLite's persistent cross-session compilation cache (patch 0006), but until emscripten-forge/recipes#6309 is resolved it is a liability: a cache=True function compiled in a session where a cache=True callee was restored from a previous session crashes with RuntimeError: no compiled object yet (#944). This affects every cached call chain (simplex_grid, the pivoting-based solvers including lemke_howson), not just comb_jit.
The constraints of the in-browser Numba build, read from the ten emscripten-forge patches and the recipe's tests — all [source]:
- The runtime is single-threaded (OpenMP and TBB disabled at build time).
target='parallel'ufuncs silently fall back to'cpu'onsys.platform == "emscripten"(patch 0007), and since patch 0010 (Fallback Numba parallel JIT to serial on Emscripten emscripten-forge/recipes#6293, merged 2026-08-18, shipped as numba 0.67.0 build 1)@njit(parallel=True)is likewise demoted to the serial pipeline:options['parallel']is forced toFalse,prangekeepsrangesemantics andNUMBA_NUM_THREADSis pinned to 1.@njit(cache=True)gains a persistent WASM object cache across browser sessions (patch 0006), butcache=Trueon@vectorize/@guvectorizeis force-disabled on Emscripten by patch 0007 — ufuncs recompile every session. Patch 0006 currently has the warm-session linking bug described above (Numba:RuntimeError: no compiled object yetemscripten-forge/recipes#6309, WASM: cache=True functions that link a cache-restored callee fail with "no compiled object yet" (simplex_grid → num_compositions_jit → comb_jit) #944).- wasm32 is a 32-bit platform: pointers,
np.intp, and NumPy's default integer are 32 bits; addressable memory is capped at 2–4 GB shared with the whole kernel. [inferred] — this follows from the target but has never been checked in a kernel, and WASM: audit 32-bit intp behaviour on wasm32 (overflow guards, simplex_index wrapping, dtypes) #929 / PR WASM: document platform-dependent intp overflow boundary (#929) #952 rest on it.
Gaps
| # | Gap | Evidence | Severity |
|---|---|---|---|
| 1 | gini_coefficient uses @njit(parallel=True) + prange |
[source] | Closed |
| 2 | support_enumeration and vertex_enumeration hang because jitted generator functions hang on the emscripten-forge build: numba requests the symbol numba_make_generator but patch 0002 exports only Numba_make_generator. Confirmed by a ctypes symbol probe and by a PythonAPI._get_function redirect that makes the generator return [0, 1, 2]. vertex_enumeration is expected to fail by the same mechanism but has not been observed |
[browser] for support_enumeration and the root cause; [inferred] for vertex_enumeration |
Blocked on upstream |
| 3 | import quantecon eagerly compiles four guvectorize ufuncs (_probvec_parallel, _probvec_cpu, _sample_without_replacement, _ints_arr_to_bits) plus one eagerly-signed @jit, comb_jit, carrying types.intp(types.intp, types.intp). ufunc caching is disabled on Emscripten (patch 0007) so the four gufuncs recompile every session. Native cost measured 2026-09-09: 1.85 s cold vs 0.65 s warm, so ~1.2 s is eager compilation. Browser cost still unmeasured; test_import_time in the smoke suite is the instrument |
[source] inventory; native timing measured; browser unmeasured | UX / performance |
| 4 | 32-bit intp on wasm32: the comb_jit / simplex_grid overflow guard trips at 2³¹−1 instead of 2⁶³−1. Correct by design (the value is an array size); simplex_index is pure Python and cannot wrap, and the tests are already intp-keyed. Only the docstrings need to state the platform-dependent boundary (#929) |
[inferred] — one line in a kernel (np.iinfo(np.intp).max) settles it, and PR #952 should not merge before that |
Documentation |
| 5 | A cache=True function compiled in a session where a cache=True callee was restored from the persistent cache fails with RuntimeError: no compiled object yet (patch 0006, emscripten-forge/recipes#6309). Deterministic for simplex_grid via num_compositions_jit → comb_jit; also exposes k_array_rank_jit → comb_jit and the _pivoting / _lex_min_ratio_test callers (lemke_howson, linprog_simplex, lcp_lemke). Only reproduces on a warm cache |
[browser] #944 | Blocked on upstream |
| 6 | New. quantecon/util/array.py runs with objmode(): inside @jit(nopython=True) to emit a DeprecationWarning from the public searchsorted helper. objmode lifts back into the CPython interpreter from compiled code and requires the GIL; none of the ten emscripten patches mentions it. Previously filed under "what does not need to change", which was too generous — the "no object-mode @jit" claim is true of the decorator but skips the context manager |
[inferred] — row 14 of #928's table, "not yet run" | Unknown until run |
| 7 | Withdrawn. Network utilities were listed as failing in the browser because WASM has no sockets. This is false. GAMReader.from_url and requests.get both work: urllib3 has shipped native Emscripten support since 2.2.0 and enables it automatically when sys.platform == "emscripten". See #931 and PR #948 |
[browser] @oyamad, 2026-09-06 | Not a gap |
Work plan
| Phase | Issue | Work item | Status (2026-09-09) |
|---|---|---|---|
| 0 — Prove it | #928 | JupyterLite proof-of-concept deployment + browser smoke suite | Config, ci/wasm/smoke_test.py and the Playwright harness on main (PR #938). Results table has 5 of 14 rows filled, all from the public demo. Nothing has run in the browser from this repo |
| 0 | #950 | Priority raised 2026-09-09 — this is now the lead item of the campaign. WASM testbed site under the QuantEcon org: a JupyterLite site on Pages whose environment can include locally patched packages, with the smoke notebooks preloaded | Not started. Two jobs, and the second is why it moved up: it replaces "paste cells into someone else's demo" as our evidence source, and it is the bench on which we design and test the patches we contribute upstream. A C-level fix cannot be validated by monkeypatching a live kernel — it needs a rebuilt package running in a browser, which is exactly what #950 provides |
| 1 — Code fixes | #926 | Replace the O(n²) parallel loop in gini_coefficient with the O(n log n) sorted closed form |
Done. PR #937 merged 2026-09-09; #926 closed. Note the smoke suite's test_gini_coefficient still carries a strict=True xfail for Emscripten that is now wrong on two counts — see #933 |
| 1 | #927 | Upstream the jitted-generator hang with a minimal repro; decide whether support_enumeration / vertex_enumeration need Emscripten-gated non-generator paths |
Blocked on upstream. The draft report in the #927 thread is still unfiled after 19 days, and anutosh491's offer to take it is still unanswered. The repro has not been re-run on 0.67.0 build 1 |
| 1 | #929 | Document the platform-dependent intp overflow boundary (docs only) |
PR #952 open and CLEAN. Hold for a one-line browser check of np.iinfo(np.intp).max before merging — the claim is very likely right, which is exactly why confirming it is cheap |
| 1 | #930 | Reduce import-time eager Numba compilation | PR #943 CLEAN, unreviewed since 2026-08-22. oyamad's alternative on wasm-930-dynamic-gufuncs still has no PR. Decision needed on which implementation goes forward. Native timing now recorded (Gap 3); the browser gate is still open |
| 1 | #931 | Document browser limitations; raise helpful platform-specific errors | Item 1 falsified — see Gap 7. PR #948 implements guards that would remove working functionality and must be cut back to the probvec docstring note (item 2), which is correct and should land. Item 3 (demote requests) is dependency hygiene with no WASM rationale and is entangled with the separate question of deprecating util/notebooks.py in 0.12.0 — move it out of this tracker. Item 4 (docs page) stands |
| 1 | #944 | cache=True caller linking a cache-restored callee fails with "no compiled object yet" |
Blocked on emscripten-forge/recipes#6309, no maintainer response in 20 days. Decision stands: wait for upstream, no library-side change |
| 2 — Distribution | #932 | Contribute a quantecon recipe to emscripten-forge |
No recipe yet; quantecon already installs from conda-forge noarch. Justify or drop: the package is pure Python with no build step, so state what the recipe buys (solve speed, pinned coverage, visibility) before spending effort on it |
| 3 — CI | #933 | Add a WebAssembly/JupyterLite job to CI | Native job on main (PR #938); browser runner PR not started. Tasks 1–6 in the body, plus two new ones: the gini strict=True xfail will hard-fail the first browser run, and coverage is not packaged for WASM |
| 4 — Ecosystem | #934 | Demo notebook, lectures without a kernel server, WASM SIMD exploration | After the intermediate release and #932. The JAX-on-emscripten funding conversation with QuantStack is tracked separately — see "Explicitly not doing" |
Sequencing. #950 leads. Two arguments put it at the front. The first is defensive: reasoning is currently cheaper than testing, which is what produced #931 — five issues that depend on browser evidence were written while the harness sat unbuilt, two became PRs, and one rested on a false premise. The second is the stronger one. Two of this campaign's five gaps are blocked on upstream fixes we have already diagnosed but cannot build or test (#927, #944). Being able to compile a patched numba and exercise it in a real kernel turns us from a reporter of upstream bugs into a contributor of upstream fixes — with browser evidence attached to the PR. That is a different relationship with emscripten-forge, and it is worth more than any single item further down this plan. #933 remains complementary rather than an alternative: it gives regression CI against the stock stack, while #950 gives the interactive, deep-linkable kernel and the patched-build bench. Both before more documentation written from inference.
Front of the plan (next session):
- Build WASM: Host a WASM testbed site under the QuantEcon org #950. The lead item. It converts the 9 "not yet run" rows into evidence, removes the dependency on a third-party demo, and — the reason it outranks everything else — gives us a bench on which to design and test upstream patches. The first candidate is ready: WASM: support_enumeration and vertex_enumeration hang in the browser — root cause is jitted generators (numba.np.linalg._LAPACK verified working) #927's generator alias. A source audit on 2026-09-09 confirms the fix is a single alias and that it is complete —
_dynfunc.c_helperscontains exactly one entry,make_generator, andnumba/core/base.pyregisters every helper from both helper dicts as"numba_" + key, soNumba_make_generatoris the only helper whose registered name differs in case from its C symbol (_helperlib's 158 are already lowercase in C; NRT's 25 register asNRT_<name>and match). Once WASM: Host a WASM testbed site under the QuantEcon org #950 exists that patch can be built, run, and sent upstream with evidence rather than offered as a diagnosis. - Rework PR WASM: raise OSError for network calls on Emscripten (#931) #948 to the
probvecdocstring only, and correct item 1 of WASM: document browser limitations and raise helpful platform-specific errors #931 so the next person is not sent down the same path. - Fix the gini
strict=Truexfail inci/wasm/smoke_test.pybefore WASM: add a WebAssembly/JupyterLite smoke-test job to CI #933's runner executes, or the first correct browser run reports red. - File the jitted-generator report upstream (WASM: support_enumeration and vertex_enumeration hang in the browser — root cause is jitted generators (numba.np.linalg._LAPACK verified working) #927) — take anutosh491's offer or file from the draft; re-run the repro on 0.67.0 build 1 first.
- Decide the WASM: reduce import-time eager Numba compilation #930 implementation — PR WASM: defer eager Numba gufunc/jit compilation to first use (#930) #943 as rebased, or a PR from
wasm-930-dynamic-gufuncs. Both satisfy the Race condition in GUFunc caching can lead to segmentation faults numba/numba#10128 constraint. Then either record the browser import timing or waive the gate explicitly.
Explicitly not doing
- No pre-emptive platform guards. Do not add a
sys.platform == "emscripten"branch that refuses to attempt an operation on the theory that it cannot work. WASM: raise OSError for network calls on Emscripten (#931) #948 is the cautionary case: the guard fired on precisely the condition that enables urllib3's browser transport. If a friendlier error is wanted, wrap a failure that actually occurs rather than refusing before trying. - Smoke tests assert capabilities, not guards. A test that asserts our own
sys.platformbranch raises is self-fulfilling and will pass in the browser while the behaviour is wrong. Assert what the platform can do. - No library-side workaround for WASM: cache=True functions that link a cache-restored callee fail with "no compiled object yet" (simplex_grid → num_compositions_jit → comb_jit) #944 — waiting on Numba:
RuntimeError: no compiled object yetemscripten-forge/recipes#6309 (decision 2026-08-21). WASM: defer eager Numba gufunc/jit compilation to first use (#930) #943 must not be cited as resolving it. - No
int64widening ofcomb_jit—intpis intentional (array size); PR WASM: use int64 in comb_jit for consistent overflow guards on wasm32 #942 closed unmerged, WASM: audit 32-bit intp behaviour on wasm32 (overflow guards, simplex_index wrapping, dtypes) #929 is docs-only. - No long-lived
wasmbranch — each phase merges tomainas it is ready (PR DOC: document multi-phase work and release procedure; add AGENTS.md #941 documents the procedure). - WASM: support_enumeration and vertex_enumeration hang in the browser — root cause is jitted generators (numba.np.linalg._LAPACK verified working) #927 and WASM: cache=True functions that link a cache-restored callee fail with "no compiled object yet" (simplex_grid → num_compositions_jit → comb_jit) #944 do not gate the intermediate release; they stay in milestone 8 for tracking only.
- No
pytest-timeout/xfailhang guards for the generator tests in the browser job — neither can interrupt a native loop in the single-threaded kernel; useskipif(IS_EMSCRIPTEN)(WASM: add a WebAssembly/JupyterLite smoke-test job to CI #933). - JAX on emscripten-forge is not a work item of this tracker. If it proceeds it gets its own issue (QuantEcon/meta) and a link from WASM: demo notebook and ecosystem coordination with QuantStack #934, not a phase here.
What does not need to change
No C extensions, no build step, no subprocess/multiprocessing/threading anywhere in the runtime package [source], no @jitclass, no generated_jit, no threading-layer APIs. All four runtime dependencies resolve in the browser ecosystem today. One correction to the previous revision: the test tooling is not entirely packaged for WASM — pytest and pandas are emscripten-forge recipes, but coverage is not, and conda-forge's coverage is not noarch because it builds a C tracer per platform [source]. #933 should plan for the pure-Python tracer or accept no coverage from the browser job.
Sources
- Blog post: Numba in the Browser: Unlocking a New Scientific Python Stack in JupyterLite (QuantStack, 11 Aug 2026)
- emscripten-forge/recipes —
recipes/recipes_emscripten/{numba,llvmlite,scipy,sympy,pytensor-base}/, especially numba patches 0002 (runtime helper exports), 0006 (persistent WASM cache), 0007 (parallel-ufunc fallback; ufunc cache disabled) and 0010 (serial fallback for@njit(parallel=True)) - Numba:
RuntimeError: no compiled object yetemscripten-forge/recipes#6309 — warm-session "no compiled object yet" bug in patch 0006, reported by @oyamad (tracked here as WASM: cache=True functions that link a cache-restored callee fail with "no compiled object yet" (simplex_grid → num_compositions_jit → comb_jit) #944) - Race condition in GUFunc caching can lead to segmentation faults numba/numba#10128 — GUFunc cache segfault when an explicit-signature and a dynamic gufunc share a kernel with
cache=True; the candidate fix at Fix race condition in GUFunc caching that causes segfaults (#10128) numba/numba#10545 is open. Constrains WASM: reduce import-time eager Numba compilation #930 - urllib3 Emscripten support — why the network calls work; Drop
requests-wasm-polyfill? emscripten-forge/recipes#782 retiredrequests-wasm-polyfillas redundant - jupyterlite-xeus environment docs
- Code audit against
main@ 13b436b (13 Aug 2026); re-checked against c4bef77, 481947d (21 Aug) and 0c7c816 (9 Sep). File references are in the sub-issues
Activity
anutosh491 commentedon Aug 13, 2026
Hi @mmcky ,
Author of the blog mentioned here :)
Feel free to reach out to the me or the emscripten-forge team at QuantStack in case you would like to collaborate on some of the packaging work here @SylvainCorlay @MMesch !
mmcky commentedon Aug 13, 2026
@anutosh491 amazing work! Great blog post -- and well done to the whole team behind this. Thanks @SylvainCorlay for reposting the blog post on LinkedIn too. It would be great to link up with the team and learn more. Thanks for saying 👋
SylvainCorlay commentedon Aug 13, 2026
We'd be happy to!
5 remaining items
mmcky commentedon Aug 21, 2026
@anutosh491 thank you — and thank you for patch 0010 (emscripten-forge/recipes#6293); we see numba 0.67.0 build 1 on emscripten-forge-4x. To answer the question properly:
gini_coefficientis our only@njit(parallel=True)user so this will be useful.I see two blockers so far for us upstream:
numba_make_generatorbut patch 0002 exports onlyNumba_make_generator, verified with ctypes and aPythonAPI._get_functionmonkeypatch. It affectssupport_enumerationandvertex_enumeration. We have a minimal@njit+yieldrepro — would you like it filed as an issue on emscripten-forge/recipes, or should we post it directly to you?RuntimeError: no compiled object yetemscripten-forge/recipes#6309 (tracked here as WASM: cache=True functions that link a cache-restored callee fail with "no compiled object yet" (simplex_grid → num_compositions_jit → comb_jit) #944): acache=Truecaller linking acache=Truecallee restored from the persistent cache fails with "no compiled object yet". We have decided to wait for the upstream change rather than alter the library.Your help on this is amazing! Thank you. Any feedback is welcome and we can work on workarounds locally if needed.
mmcky commentedon Aug 21, 2026
Correction to the release-sequencing note above (2026-08-19), now that the Phase 1 sub-issues have settled: the release-gating code changes are #926 (PR #937, approved, awaiting merge) and #930 (PR #943, after a rebase onto
mainthat drops #942's int64 commit and keepscomb_jitasintp). #929 and #931 are documentation. #927 (jitted-generator hang) and #944 (see emscripten-forge/recipes#6309) are blocked on upstream and must not gate the intermediate release — they stay in milestone 8 but are scoped out of the release checklist, so the release fires when #926 and #930 close, not when every Phase 1 issue does.WASM: defer eager Numba gufunc and jit compilation to first use
WASM: defer eager Numba gufunc compilation to first use
anutosh491 commentedon Aug 27, 2026
Hey @mmcky ,
I just got jax jit working in Jupyterlite yesterday
Screen.Recording.2026-08-25.at.2.42.24.PM.mp4
Screen.Recording.2026-08-25.at.9.35.27.PM.mp4
Is this something QuantEcon would like to make use of ?
if yes, I would like to know if there might be interest from y'all to fund pacakging jax on emscripten-forge !?
There are quite some patches to maintain here and this would be even more challenging that our numba recipe. Just thought of asking if this is something that interests y'all.
mmcky commentedon Aug 27, 2026
@anutosh491 WOW! That's amazing! Yes absolutely something we would like to use :-).
How does this magic work?!
I imagine this is a
cpulink (rather thangpupassthrough). But that would unlock a lot for teaching jax on our jax.quantecon.org series. I'd love to run some benchmarks on this.mmcky commentedon Aug 27, 2026
Let's link up and discuss this possibility. I'm our only full time technical (programming + CS tasks) resource but we have a network of RAs I could work with.
anutosh491 commentedon Aug 27, 2026
Hey Matt,
Thanks a lot for the email. Waiting for Sylvain to suggest a suitable time there.
Now that you've highlighted benchmarks, people have already started playing with my jax build and have told me what works and what doesn't. So I do have an idea of what things to improve for jax :)
See comments :
pyodide/pyodide-recipes#187 (comment)
pyodide/pyodide-recipes#187 (comment)
mmcky commentedon Aug 31, 2026
Revision log, 2026-08-31 (body re-stamped
verified 2026-08-31 14:53 AEST; previous stamp 2026-08-21). This is a trace sweep across the sub-issues, PRs,main, emscripten-forge and numba since 2026-08-21 — no code or issue work was done in the session that wrote it, so everything below is attributed to whoever's trace it is.What happened since 2026-08-21
comb_jitcommit is gone and the PR no longer touchescomb_jitat all — all four gufuncs become lazily constructed explicit-signature gufuncs. Checks green, CLEAN, no review yet.wasm-930-dynamic-gufuncs(7c512ed, 2026-08-23) to this repo — a second WASM: reduce import-time eager Numba compilation #930 implementation: dynamic gufuncs for_sample_without_replacementand_ints_arr_to_bits,functools.cache-wrapped explicit-signature gufuncs for both_probvecvariants,requires-pythonbumped to>=3.9. No PR is open for it.cache=Truesegfault (Race condition in GUFunc caching can lead to segmentation faults numba/numba#10128; fix Fix race condition in GUFunc caching that causes segfaults (#10128) numba/numba#10545 is open, unmerged). Consequence:_probvec_cpucannot go dynamic while_probvec_parallelkeeps its explicit signature. Both candidate implementations already respect this.main, carriesCloses #926. Nothing has changed on it since 2026-08-20.mainhas not moved: 481947d (PR WASM: JupyterLite environment config and browser smoke suite #938).RuntimeError: no compiled object yetemscripten-forge/recipes#6309 has had no maintainer response since oyamad's repro and candidate fix on 2026-08-20; no report of the jitted-generator hang exists anywhere — the WASM: support_enumeration and vertex_enumeration hang in the browser — root cause is jitted generators (numba.np.linalg._LAPACK verified working) #927 draft is still unfiled, and anutosh491's 2026-08-21 offer here to take it has not been answered.jitnow runs in JupyterLite and asked whether QuantEcon would help fund packaging JAX on emscripten-forge; mmcky replied yes in principle and a call with SylvainCorlay is being arranged by email.What the body now says differently
## Where we standheading now carries the verified stamp in the tracker-contract form and a stamped live-state table (the 2026-08-21 body had only theUpdatedbanner and an undated heading).wasmbranch, WASM: support_enumeration and vertex_enumeration hang in the browser — root cause is jitted generators (numba.np.linalg._LAPACK verified working) #927/WASM: cache=True functions that link a cache-restored callee fail with "no compiled object yet" (simplex_grid → num_compositions_jit → comb_jit) #944 not release-gating, nopytest-timeout/xfailhang guards) and adds one: JAX-on-emscripten-forge funding is not a work item of this tracker — if it proceeds it gets its own issue next to meta#143 and a link from WASM: demo notebook and ecosystem coordination with QuantStack #934.Resume pointer — in order: (1) merge PR #937, which closes #926; (2) decide the #930 implementation (PR #943 vs a PR from
wasm-930-dynamic-gufuncs) and either record the import timing or waive that gate; (3) file the jitted-generator repro on emscripten-forge/recipes — take anutosh491's offer or file from the #927 draft, after re-running it on 0.67.0 build 1; (4) open the #933 runner PR from the parked job. Sub-issue progress is 0/10; the dashboard reads this tracker asuntypedbecause it has noProjectissue type.mmcky commentedon Sep 9, 2026
Revision log — 2026-09-09. The body above was rewritten after an adversarial re-check of every claim in it against live state. The previous text is in the edit history. What changed, and why.
A claim was falsified. #931's first item held that the network utilities cannot work in the browser because WASM has no sockets. @oyamad demonstrated on 2026-09-06 that
GAMReader.from_urlandrequests.getboth work in xeus-python. urllib3 has shipped native Emscripten support since 2.2.0 and auto-enables it whensys.platform == "emscripten";requestsinherits it; emscripten-forge retiredrequests-wasm-polyfillas redundant. That claim reached a merge-ready PR (#948) before anyone tested it, and the guard it produced fires on precisely the condition that enables the browser transport. It is now Gap 7, marked withdrawn, and #931 is corrected.An evidence convention was added, because the above is a class of error rather than a one-off. Every claim now carries
[browser],[source]or[inferred], with the run or the file cited. #927 was already doing this — its "by mechanism; not yet observed" note onvertex_enumerationis the model, and #927 is correspondingly the one item nobody has had to re-litigate.Three corrections to the previous revision.
coverageis not packaged for WASM (not among emscripten-forge's 639 recipes; conda-forge's is notnoarchbecause it builds a C tracer), so the "test tooling is packaged for WASM" claim is wrong for it. The dependency list is now four, not five — PR #947 removed sympy. Andobjmodewas filed under "what does not need to change" on the strength of "no object-mode@jit"; that is true of the decorator but skipswith objmode():inside a nopython jit inquantecon/util/array.py, which is now Gap 6.Two things measured or resolved. Gap 1 is closed — PR #937 merged, so #926 is closed. And #930's import-time gate, unrecorded since 13 August, now has a native reference point:
import quanteconat 0c7c816 takes 1.85 s cold against 0.65 s with the Numba on-disk cache warm, so about 1.2 s is eager compilation. The inventory is exactly as claimed — four gufuncs plus the eagerly-signedcomb_jit. Native seconds are not browser seconds; this is a lower bound that confirms the item is real, not the gate itself.#950 joins the plan. It has been a linked sub-issue since 2026-09-07, but the body and the work plan never absorbed it. It matters more than its age suggests: it is the only way to exercise a patched numba build, which is what #927's candidate generator alias needs, since a C-level fix cannot be validated by monkeypatching a live kernel.
Sequencing changed. #950 and #933 move ahead of the documentation work. They are complementary — #933 gives regression CI on the stock stack, #950 gives a deep-linkable kernel where a claim is checkable in seconds. The reason to prioritise them is structural: reasoning is currently cheaper than testing, #928's evidence table still has 9 of 14 rows marked "not yet run", and no browser run has ever come from this repository's CI. Five issues that depend on browser evidence were written in the meantime and two became PRs. Notes have gone to #931 and #933 with the specifics, including a
strict=Truexfail on gini that will hard-fail the first browser run.mmcky commentedon Oct 4, 2026
Revision log — 2026-10-05. The Where we stand section was re-verified row by row against live state and re-dated; nothing else in the body changed. Recorded: PR #957 merged on 2026-09-23 and shipped in v0.12.0, #943 closed in its favour, and #948 now conflicts with
main. The section now names the two upstream waits (#944 on emscripten-forge/recipes#6309, and #927, whose upstream report is still unfiled) and says that #950 remains the lead item, not started. The Gap 3 and #930 work-plan rows predate #957 and are flagged as such rather than rewritten.mmcky commentedon Oct 17, 2026
For the record: QuantEcon/QuantEcon.py-wasm#13 notes that
quantecon_wasmstill carries copies of theLQMarkovandARMAbugs fixed in v0.13.1 (#983, #976), and asks whether to port the fixes there or retire that package in favour of the browser support tracked here.