Conversation
`import quantecon` compiled four `guvectorize` functions eagerly because of their explicit type signatures. In the in-browser (Emscripten) Numba build, where `cache=True` is disabled for gufuncs, that cost was paid on every session on the import path. Defer all four to first call: * `_ints_arr_to_bits` and `_sample_without_replacement` become dynamic (signature-less) gufuncs; the callers now supply the output array. * `_probvec_parallel` and `_probvec_cpu` are constructed on first call by `functools.cache`d factories, keeping their explicit signatures. The parallel target requires one, and the cpu variant keeps one too: an explicit-signature gufunc and a dynamic gufunc of the same kernel share an incompatible cache entry when `cache=True`, which crashes the process. Public API is unchanged. `functools.cache` requires Python 3.9, so `requires-python` is raised from 3.7 to 3.9. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two adjustments to the cherry-picked implementation, neither changing its behaviour. `functools.cache` is an alias for `functools.lru_cache(maxsize=None)` added in 3.9 purely for convenience, so using `lru_cache` directly gives identical memoisation without imposing a version floor. That removes the need for the `requires-python` bump to 3.9, which is reverted here: the floor is stale at ">=3.7" while CI tests 3.12-3.14, but correcting it is #864's business and does not belong in a WASM change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Sep 9, 2026
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
The changes are localized, call sites appear consistently updated, and no correctness issues were found in the modified regions.
Pull request overview
Defers several Numba guvectorize compilations from import time to first call to reduce import quantecon overhead (especially relevant for WASM/JupyterLite where persistent ufunc caching is unavailable).
Changes:
- Replaces module-level
_probvec_parallel/_probvec_cpugufunc objects with lazily-constructed,lru_cache-memoized factories. - Converts
_sample_without_replacementand_ints_arr_to_bitsto dynamic (signature-less) gufuncs, with callers supplying the output arrays. - Updates internal call sites to use the new factory / output-supplied calling patterns.
File summaries
| File | Description |
|---|---|
quantecon/random/utilities.py |
Lazily constructs the _probvec_* gufuncs; makes _sample_without_replacement a dynamic gufunc with caller-supplied output. |
quantecon/game_theory/vertex_enumeration.py |
Updates _ints_arr_to_bits to a dynamic gufunc and updates the caller to provide an output buffer. |
quantecon/game_theory/random.py |
Updates _probvec_cpu usage to call the new memoized factory. |
Review details
- Files reviewed: 3/3 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Resolves two docstring conflicts with #864 by keeping this branch's text: the `_sample_without_replacement` docstring (rewritten here; already spells "compiled") and the `_ints_arr_to_bits` example (the 1-D-to-scalar form removed here no longer needs its repr updated). `requires-python` takes main's ">=3.12". Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Member
|
@mmcky Thanks! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Defers the four eagerly-compiled Numba gufuncs to first call, so they are no longer compiled during
import quantecon. Part of #930, part of #925 (Phase 1).This is @oyamad's implementation from the
wasm-930-dynamic-gufuncsbranch, opened as a PR in place of #943, which is closed. His commit is preserved as authored; the second commit carries two small adjustments described below.What changes
_probvec_parallelrandom/utilities.py_probvec_cpurandom/utilities.py_sample_without_replacementrandom/utilities.py_ints_arr_to_bitsgame_theory/vertex_enumeration.pyThe explicit signature is kept for both
_probvectargets deliberately, and the code now says why: the parallel target requires it, and an explicit-signature gufunc must not be mixed with a dynamic one of the same kernel undercache=True, because they share an incompatible cache entry (numba#10128). That constraint was previously undocumented.No public API change.
qe.random.probvec(..., parallel=...),qe.random.sample_without_replacement(...)andqe.game_theory.vertex_enumeration(...)are untouched.The two adjustments
functools.cache→functools.lru_cache(maxsize=None). The former is an alias for the latter, added in 3.9 for convenience only, so this is identical memoisation without a version floor.requires-pythonbump to>=3.9is reverted. The floor is stale at>=3.7while CI tests 3.12–3.14, but fixing that is DOC: repo-wide documentation consistency audit #864's business and does not belong in a WASM change.Verification
flake8 --select=F401,F405,E231 quantecon: cleanimport quantecon, both_probvecfactories reportcache_info().currsize == 0; after oneprobveccall they are populatedimport quantecon, best of 7: 0.70 s onmain→ 0.65 s hereThe measurement this still needs
#930's acceptance criterion 1 — warm-cache
import quantecontimed in JupyterLite, before and after — is not satisfied by the native number above, and remains open. It matters more than it looks: emscripten-forge Numba patch 0007 forcescache=Falsefor every@guvectorize, so in the browser these four compiles are still paid once per session either way. This change moves that cost from import to first call rather than removing it, which is a clear win only if most notebooks never reachprobvec,sample_without_replacementorvertex_enumeration.For that reason this PR says Part of #930, not "Closes". Either a manual JupyterLite timing settles criterion 1, or a maintainer waives it explicitly on #930, as that issue requires.
Not fixed here
#944 (
RuntimeError: no compiled object yetwhen a cache-miss caller links a cache-restored callee, reported upstream as emscripten-forge/recipes#6309) is unaffected. This change narrows the exposure but does not resolve it, and should not be cited as doing so.Part of #930. Replaces #943.
🤖 Generated with Claude Code