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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions