You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Updated 2026-08-21. Second revision today, after the cross-issue consistency pass: vertex_enumeration joins the scope — _vertex_enumeration_gen is the package's only other @jit(nopython=True) generator, so it is expected to hang for the same reason (by mechanism; not yet observed in the browser) — and the status note, the Problem section, the acceptance criteria and a new Related section are updated accordingly, now that PR #938 has merged and ci/wasm/smoke_test.py exists on main. The former "Side benefit" section is removed because its premise was wrong: _indiff_mixed_action, which makes the _LAPACK call, is already cache=True, and the # cache=True raises _pickle.PicklingError comment on the generator is a 2016 leftover unrelated to _LAPACK (cache=True on the generator works natively on Numba 0.62.1; re-enabling it is a separate maintenance item). Two permalinks and the patch count are corrected in place, and a draft of the upstream report for criterion 3 is in the comment below.
Status update (2026-08-21). The _LAPACK question is answered: on the public numba-ecosystem JupyterLite demo (Numba 0.66.0 / NumPy 2.4.6 / SciPy 1.18.0 on Emscripten) both bare np.linalg.solve inside @njit and QuantEcon's _numba_linalg_solve return correct results (#927 (comment)). support_enumeration still never returns, and the culprit is Numba generator functions — a minimal @njit function with yield hangs on first call. support_enumeration_gen is exactly such a generator, so the blocker has moved from LAPACK to jitted generators. vertex_enumeration / vertex_enumeration_gen is affected by the same bug: _vertex_enumeration_gen (yield at L161) is the package's only other jitted generator, and the failure happens at generator creation, before its numba.typed.Dict use is ever reached — so both game_theory enumeration routines are blocked until the upstream export is fixed. Original issue text follows; the "If it fails" fallback-solver plan is no longer needed and the acceptance criteria have been revised at the bottom.
Part of #925 (Phase 1, gated on the Phase 0 findings in #928). This is the highest-risk unknown in the browser-compliance audit.
Problem
util/numba.py imports the private API from numba.np.linalg import _LAPACK and uses it in an @overload to bind LAPACK's xgesv (Fortran, reached through SciPy's cython_lapack capsules) as a raw function address callable from nopython code. In game_theory.support_enumeration, _indiff_mixed_action calls it from inside the jitted equilibrium search, reached from the jitted generator _support_enumeration_gen.
Natively, the bound address is a machine-code pointer. Under Emscripten it must be an index into the shared WebAssembly function table, resolved across dynamically loaded side modules (SciPy's extension modules on one side, Numba's freshly linked user module on the other), with a matching signature at the call_indirect site. The QuantStack engine was explicitly designed for cross-module symbol resolution, so this may just work — but none of the (now ten) emscripten-forge numba patches mention LAPACK or linalg, and the recipe's tests never exercise np.linalg inside a jitted function.
game_theory.vertex_enumeration never touches _LAPACK, but it is driven by a jitted generator of the same kind — _vertex_enumeration_gen, returned by vertex_enumeration_gen and consumed by vertex_enumeration — which is why it is in scope now that the hang is traced to generators rather than to the LAPACK binding.
How to test
The same _LAPACK mechanism backs Numba's own np.linalg.solve support, so the clean experiment (part of the Phase 0 smoke suite, #928) is: run np.linalg.solve inside a scratch @njit function in the browser. If that works, QuantEcon's helper almost certainly works too; if it fails, both fail together.
If it fails
In order of preference:
A pure-Numba LU solve with partial pivoting behind a sys.platform == "emscripten" gate. support_enumeration solves small dense (k+1)×(k+1) systems for support size k, so a hand-written nopython solver is entirely adequate there.
Report the gap upstream to the emscripten-forge / Numba WASM effort — Numba's own linalg support has the same dependency, and the authors are actively expanding coverage.
Related
PR WASM: JupyterLite environment config and browser smoke suite #938 (merged 2026-08-21): ci/wasm/environment.yml, ci/wasm/smoke_test.py, ci/wasm/test_jupyterlite.py and the native ci_wasm_smoke.yml job are now on main. test_support_enumeration and test_vertex_enumeration in smoke_test.py are unguarded and will hang in the browser, and test_jupyterlite.py runs test_support_enumeration in its shared kernel; guarding them with something that does not execute the test, and adding the minimal generator test from criterion 2, is part of the WASM: add a WebAssembly/JupyterLite smoke-test job to CI #933 runner work.
Generator hang reported upstream to the emscripten-forge / Numba WASM effort with the minimal repro (draft report in the comment below)
Decide whether support_enumeration and vertex_enumeration need an Emscripten-gated non-generator path, or whether we wait on the upstream fix
In-browser tests cover support_enumeration and vertex_enumeration (test_support_enumeration and test_vertex_enumeration in ci/wasm/smoke_test.py, currently unguarded) so regressions are caught once they run
Note
Updated 2026-08-21. Second revision today, after the cross-issue consistency pass:
vertex_enumerationjoins the scope —_vertex_enumeration_genis the package's only other@jit(nopython=True)generator, so it is expected to hang for the same reason (by mechanism; not yet observed in the browser) — and the status note, the Problem section, the acceptance criteria and a new Related section are updated accordingly, now that PR #938 has merged andci/wasm/smoke_test.pyexists on main. The former "Side benefit" section is removed because its premise was wrong:_indiff_mixed_action, which makes the_LAPACKcall, is alreadycache=True, and the# cache=True raises _pickle.PicklingErrorcomment on the generator is a 2016 leftover unrelated to_LAPACK(cache=Trueon the generator works natively on Numba 0.62.1; re-enabling it is a separate maintenance item). Two permalinks and the patch count are corrected in place, and a draft of the upstream report for criterion 3 is in the comment below.Part of #925 (Phase 1, gated on the Phase 0 findings in #928). This is the highest-risk unknown in the browser-compliance audit.
Problem
util/numba.pyimports the private APIfrom numba.np.linalg import _LAPACKand uses it in an@overloadto bind LAPACK'sxgesv(Fortran, reached through SciPy'scython_lapackcapsules) as a raw function address callable from nopython code. Ingame_theory.support_enumeration,_indiff_mixed_actioncalls it from inside the jitted equilibrium search, reached from the jitted generator_support_enumeration_gen.Natively, the bound address is a machine-code pointer. Under Emscripten it must be an index into the shared WebAssembly function table, resolved across dynamically loaded side modules (SciPy's extension modules on one side, Numba's freshly linked user module on the other), with a matching signature at the
call_indirectsite. The QuantStack engine was explicitly designed for cross-module symbol resolution, so this may just work — but none of the (now ten) emscripten-forge numba patches mention LAPACK or linalg, and the recipe's tests never exercisenp.linalginside a jitted function.game_theory.vertex_enumerationnever touches_LAPACK, but it is driven by a jitted generator of the same kind —_vertex_enumeration_gen, returned byvertex_enumeration_genand consumed byvertex_enumeration— which is why it is in scope now that the hang is traced to generators rather than to the LAPACK binding.How to test
The same
_LAPACKmechanism backs Numba's ownnp.linalg.solvesupport, so the clean experiment (part of the Phase 0 smoke suite, #928) is: runnp.linalg.solveinside a scratch@njitfunction in the browser. If that works, QuantEcon's helper almost certainly works too; if it fails, both fail together.If it fails
In order of preference:
sys.platform == "emscripten"gate.support_enumerationsolves small dense (k+1)×(k+1) systems for support size k, so a hand-written nopython solver is entirely adequate there.Related
ci/wasm/environment.yml,ci/wasm/smoke_test.py,ci/wasm/test_jupyterlite.pyand the nativeci_wasm_smoke.ymljob are now on main.test_support_enumerationandtest_vertex_enumerationinsmoke_test.pyare unguarded and will hang in the browser, andtest_jupyterlite.pyrunstest_support_enumerationin its shared kernel; guarding them with something that does not execute the test, and adding the minimal generator test from criterion 2, is part of the WASM: add a WebAssembly/JupyterLite smoke-test job to CI #933 runner work.cache=Truecallers of cache-restored functions fail with "no compiled object yet", reported upstream as Numba:RuntimeError: no compiled object yetemscripten-forge/recipes#6309). Different mechanism, same "wait for upstream" posture.support_enumerationand bare-generator rows belong).Acceptance criteria
np.linalg.solveinside@njitworks on Emscripten (LAPACK binding resolves correctly)ci/wasm/smoke_test.py, on main since PR WASM: JupyterLite environment config and browser smoke suite #938) so the mechanism is isolated the way the barenp.linalg.solvetest isolates LAPACKsupport_enumerationandvertex_enumerationneed an Emscripten-gated non-generator path, or whether we wait on the upstream fixsupport_enumerationandvertex_enumeration(test_support_enumerationandtest_vertex_enumerationinci/wasm/smoke_test.py, currently unguarded) so regressions are caught once they run