Skip to content

WASM: JupyterLite proof-of-concept deployment and browser smoke suite #928

Open

Description

@mmcky

Note

Updated 2026-08-21. Phase 0 was overtaken by ad-hoc browser runs: the _LAPACK question (#927) was answered on the public numba-ecosystem demo, the intp question (#929) was settled in the #942 review, and PR #938 (merged as 481947d) has landed the deployment config and smoke suite under ci/wasm/. The checklist below is reworded to match those findings and the upstream Numba change that removed the expected gini_coefficient failure (patch 0010, emscripten-forge/recipes#6293); items with recorded browser evidence are ticked and a results table is maintained in the comments. The original text is preserved in the edit history.

Part of #925 (Phase 0). This issue was meant to gate #927 and calibrate #929 and #930; #927 was resolved by ad-hoc browser runs and #929 by the #942 review instead (see the checklist), so its remaining gating value is the cold/warm import timing for #930 and the first full-suite browser run via #933.

Goal

Prove the stack end-to-end with zero code changes, and convert every "should work" in the audit into a known. Because quantecon is noarch: python on conda-forge, there is a real chance it installs and imports in JupyterLite today with no packaging work.

Setup

Build a minimal JupyterLite deployment with jupyterlite-xeus:

name: qe-lite
channels:
  - https://prefix.dev/emscripten-forge-4x
  - https://prefix.dev/conda-forge
dependencies:
  - xeus-python
  - numba
  - quantecon

This file is now committed as ci/wasm/environment.yml (PR #938). numba is unpinned, so a fresh build resolves the current emscripten-forge-4x build — numba 0.67.0 build 1 as of 2026-08-18, which carries patch 0010 (emscripten-forge/recipes#6293) forcing @njit(parallel=True) to the serial pipeline on Emscripten. Note that the browser results recorded so far did not come from this deployment: they came from the public numba-ecosystem demo (Numba 0.66.0) and the #942 review, so the versions per result are recorded in the table.

Smoke checklist

One representative function per Numba feature class used in the library:

Deliverables

Coverage of the browser harness

What actually runs on Emscripten today is test_jupyterlite.py (driven by the job parked in #933), and it covers 5 of the 13 checklist items: import quantecon (untimed), tauchen, bare np.linalg.solve in @njit, support_enumeration and gini_coefficient. smoke_test.py is not yet shipped into the kernel, so rouwenhorst, MarkovChain.simulate, probvec, sample_without_replacement, the three optimize routines, lemke_howson, vertex_enumeration, simplex_grid, searchsorted and the import timing have no browser test until #933 wires in-kernel execution of smoke_test.py. A first green run of the parked job therefore fills at most 5 rows of the table, not 13.

Several of the committed tests also encode expectations that are already out of date; #933 (the runner job) currently notes only the test_support_enumeration hang, so the rest are being handed to it as follow-ups: test_gini_coefficient (xfail(IS_EMSCRIPTEN, strict=True)) and test_gini_fails_on_emscripten expect a failure that patch 0010 has removed; test_support_enumeration and test_vertex_enumeration have no hang guard; test_simplex_grid cites #929 where it should cite #944.

Related

Activity

  1. mmcky commented on Aug 21, 2026

    @mmcky
    ContributorAuthor

    Results table as of 2026-08-21, one row per checklist item. Only recorded browser evidence is entered; everything else is "not yet run". None of these came from the ci/wasm/environment.yml deployment — they came from the public numba-ecosystem demo (https://notebook.link/@anutosh491/numba-ecosystem) and from the #942 review — so the environment column matters: a fresh qe-lite build now resolves numba 0.67.0 build 1 (patch 0010, emscripten-forge/recipes#6293), which is newer than anything tested so far.

    Item Result Where recorded Environment
    import quantecon works (implicit: the _numba_linalg_solve and simplex_grid reproducers below import it first); cold/warm timing not yet run #927 (comment) (kp992, 2026-08-15), #927 (comment) (oyamad, 2026-08-19), #942 (comment) (kp992, 2026-08-20) numba 0.66.0 / numpy 2.4.6 / scipy 1.18.0 on Emscripten for the 2026-08-19 run; versions not recorded for the other two
    qe.tauchen / qe.rouwenhorst not yet run not yet run —
    MarkovChain.simulate not yet run not yet run —
    qe.random.probvec not yet run not yet run —
    qe.random.sample_without_replacement not yet run not yet run —
    optimize: nelder_mead, brent_max, newton not yet run not yet run —
    game_theory.lemke_howson not yet run not yet run —
    game_theory.vertex_enumeration not yet run; expected to hang (_vertex_enumeration_gen is a jitted generator, same mechanism as support_enumeration) not yet run —
    game_theory.support_enumeration fails: first call never returns (jitted generator, not LAPACK) #927 (comment) (oyamad, 2026-08-19) numba 0.66.0 / numpy 2.4.6 / scipy 1.18.0, Emscripten, 2026-08-19
    minimal @njit generator (for i in range(n): yield i) fails: hangs on first call; numba_make_generator is not exported by _dynfunc (only Numba_make_generator is); redirecting the name in PythonAPI._get_function makes it return [0, 1, 2] #927 (comment), #927 (comment), #927 (comment) (oyamad, 2026-08-19) numba 0.66.0 / numpy 2.4.6 / scipy 1.18.0, Emscripten, 2026-08-19
    np.linalg.solve inside a scratch @njit works: [2., -2.] (correct); _numba_linalg_solve also works #927 (comment) (kp992, 2026-08-15), #927 (comment) (oyamad, 2026-08-19) numba 0.66.0 / numpy 2.4.6 / scipy 1.18.0, Emscripten, 2026-08-19; versions not recorded for the 2026-08-15 run
    qe.gini_coefficient not yet run; expected to run serially on numba 0.67.0 build 1 (patch 0010), so the failure the original item expected should not appear — needs a timing as well as a pass not yet run —
    simplex_grid fails before any computation: RuntimeError: no compiled object yet for <Library 'comb_jit'> at qe.simplex_grid(3, 4); per the upstream diagnosis it needs a warm persistent cache (cache-miss cache=True caller linking a cache-hit callee); the intp boundary check was never reached #942 (comment) (kp992, 2026-08-20), confirmed at #942 (comment) (oyamad, 2026-08-20); tracked in #944, upstream emscripten-forge/recipes#6309 xeus-python JupyterLite kernel, Python 3.13 per the traceback; numba version not recorded
    quantecon.util.array.searchsorted not yet run not yet run —

    The parked #933 harness (test_jupyterlite.py) can fill five of these rows (import, tauchen, np.linalg.solve, support_enumeration, gini); the rest wait on smoke_test.py being shipped into the kernel under #933.

  2. mmcky commented on Sep 9, 2026

    @mmcky
    ContributorAuthor

    Results table delta — 2026-09-09. One new row of browser evidence, two rows whose expectations have changed, and a correction. The 2026-08-21 table above is otherwise unchanged; this comment is the delta rather than a re-typing of all fourteen rows.

    New row. The network utilities were never on the checklist, because #931 asserted they could not work. They do, and this is now the sixth row of recorded browser evidence.

    Item Result Where recorded Environment
    GAMReader.from_url and requests.get works: from_url read a .gam file from raw.githubusercontent.com and returned a full 3-player game; requests.get returned 200 with 4117 bytes #931 (comment) (oyamad, 2026-09-06) xeus-python on the numba-ecosystem demo; versions not recorded

    Two rows whose expectations changed, both still unrun. qe.gini_coefficient — PR #937 merged 2026-09-09, so the function is now the O(n log n) closed form and no longer uses @njit(parallel=True) at all; the row should expect a pass plus a timing, and smoke_test.py's strict=True xfail on it will report a failure on the first browser run until it is removed (raised in #933). quantecon.util.array.searchsorted — this row is more interesting than it looked: it exercises with objmode(): inside a nopython jit, which lifts back into the CPython interpreter and needs the GIL, and no emscripten patch mentions it. It is now Gap 6 in #925 rather than something assumed fine.

    Correction to the count. The tally has been quoted elsewhere today as "4 of 14 rows with evidence, 10 not yet run". The table above actually has 5 of 14 with evidence and 9 not yet run; with the new row it is 6 of 15 and 9. #925, and the notes on #931 and #933, are corrected.

    The nine unrun rows are what #950 and #933 exist to fill, and every one of the six recorded results came from a public demo run by hand rather than from anything this repository controls.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    infrastructureCI, build, packaging, and repo toolingtests

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions