Skip to content

WASM: audit 32-bit intp behaviour on wasm32 (overflow guards, simplex_index wrapping, dtypes) #929

Description

@mmcky

Note

Updated 2026-08-21 (second edit). Tracker reconciliation: PR #942 is still open with a live "Fixes #929" and is to be closed unmerged; PR #943 is stacked on it and carries the same int64 widening, so it must be rebased onto main keeping intp before merge. The earlier note and the #944 summary below now attribute the browser failure to the cache-hit/cache-miss linking bug in the emscripten-forge Numba build (emscripten-forge/recipes#6309), not to comb_jit's eager signature, and the grep-sweep task names k_array_rank_jit as sharing the intp boundary. Labels changed from tests to documentation.

Note

Updated 2026-08-21. The original body described three problems; review of #942 showed two of them were wrong and the third is a documentation task, not a code change. The comb_jit / simplex_grid use of np.intp is intentional and should be kept — see below. The genuine WASM blocker surfaced by #942 (cache=True callers failing to link cache-restored callees on the emscripten-forge Numba build) is tracked separately in #944. The original text is preserved in the edit history.

Part of #925 (Phase 1). Verification runs on the Phase 0 deployment (#928).

Background

Emscripten/wasm32 is a 32-bit platform: np.intp and NumPy's default integer are int32, so the library behaves as it would on 32-bit Linux. This issue audits what that means for QuantEcon.py.

Findings

comb_jit / simplex_gridintp is correct by design; document the boundary, don't widen it. comb_jit is typed intp(intp, intp) and returns 0 when the result would exceed np.iinfo(np.intp).max — 2³¹−1 in the browser instead of 2⁶³−1. Its result is consumed as an array size (np.empty((L, m)) in simplex_grid) and as a rank in k_array_rank_jit, so intp is exactly the right width: it is the largest value that can be a shape on the platform. Widening to int64 would let L ∈ [2³¹, 2⁶³) past the guard and into np.empty, trading the current clean ValueError('Maximum allowed size exceeded') for a wrap or an obscure allocation failure. Nothing real is lost either: with m ≥ 2, any grid with L ≥ 2³¹ needs ≥ 16 GB, four times wasm32's whole address space. The only action is to state the platform-dependent boundary in the docstrings.

simplex_index does not wrap. The original body claimed it accumulates comb_jit terms with wrapping intp arithmetic. It is a pure-Python function that uses num_compositions (SciPy's exact comb), so it works in unbounded Python ints. No change needed.

Test assertions are already platform-agnostic. The original body asked for dtype == np.int64 assertions to be made platform-agnostic. TestCombJit already keys off np.iinfo(np.intp), and the only int64 assertions in the suite (test_game_converters.py) round-trip an explicit dtype=np.int64 argument. No change needed.

Result dtypes shift (informational). Jitted code allocating with np.int_ / np.intp (the draw overload in random/utilities.py, index arrays in _gridtools.py) returns int32 arrays in the browser. That is correct behaviour. Where the contract is int64 via explicit i8 gufunc signatures (sample_without_replacement), NumPy's safe casting absorbs int32 inputs. Worth one sentence in the WASM docs; no code change.

Tasks

  • Docstrings for comb_jit, num_compositions_jit and simplex_grid: say the overflow boundary is np.iinfo(np.intp).max and therefore platform-dependent (2³¹−1 on wasm32)
  • Document the wasm32 limits in the JupyterLite user-facing notes: moved grid-size boundary, int32 default integer, 2–4 GB memory cap shared with the whole kernel (coordinate with WASM: document browser limitations and raise helpful platform-specific errors #931)
  • Grep sweep of remaining intp arithmetic sites — starting with k_array_rank_jit (quantecon/util/combinatorics.py:103), which calls comb_jit and so shares the same np.iinfo(np.intp).max boundary — to confirm none has a non-size/index use where int64 would be the better contract (expected outcome: none)

Related

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions