Problem
quantecon/util/random.py:9 imports a helper from a private SciPy module:
from scipy._lib._util import rng_integers # noqa: F401
scipy._lib carries no stability guarantee — SciPy may rename, move, or delete rng_integers in any release, including a patch release. Because our requirement is an unbounded scipy>=1.5.0, the day that happens every fresh install of quantecon breaks at import of the affected modules, with no action on our side. The noqa on the import line is the linter having warned us already.
The symbol is re-exported via quantecon/util/__init__.py and used at 13 call sites in 8 modules: markov/core.py:510; game_theory/normal_form_game.py:416; game_theory/logitdyn.py:112,153; game_theory/brd.py:115; game_theory/localint.py:115,180; game_theory/random.py:178; game_theory/game_generators/bimatrix_generators.py:258,262,596,612,614.
Proposed change
Vendor the function instead of importing it — the same pattern this repo already uses for copy_if_needed in quantecon/util/compat.py (copied from SciPy with attribution rather than imported):
- Copy the (small)
rng_integers implementation into quantecon/util/random.py with a provenance comment and SciPy license attribution.
- Keep the name and signature identical —
quantecon.util.rng_integers — so zero call sites change. Behavior-preserving by construction.
- Add a smoke test pinning the contract (dtype, inclusive/exclusive endpoints,
RandomState vs Generator dispatch) so future drift is caught here, not by users.
Acceptance criteria
From the July 2026 technical-debt audit (AI-assisted; claims verified against 28d4b3b on 2026-07-25).
Problem
quantecon/util/random.py:9imports a helper from a private SciPy module:scipy._libcarries no stability guarantee — SciPy may rename, move, or deleterng_integersin any release, including a patch release. Because our requirement is an unboundedscipy>=1.5.0, the day that happens every fresh install of quantecon breaks at import of the affected modules, with no action on our side. Thenoqaon the import line is the linter having warned us already.The symbol is re-exported via
quantecon/util/__init__.pyand used at 13 call sites in 8 modules:markov/core.py:510;game_theory/normal_form_game.py:416;game_theory/logitdyn.py:112,153;game_theory/brd.py:115;game_theory/localint.py:115,180;game_theory/random.py:178;game_theory/game_generators/bimatrix_generators.py:258,262,596,612,614.Proposed change
Vendor the function instead of importing it — the same pattern this repo already uses for
copy_if_neededinquantecon/util/compat.py(copied from SciPy with attribution rather than imported):rng_integersimplementation intoquantecon/util/random.pywith a provenance comment and SciPy license attribution.quantecon.util.rng_integers— so zero call sites change. Behavior-preserving by construction.RandomStatevsGeneratordispatch) so future drift is caught here, not by users.Acceptance criteria
grep -rn "scipy._lib" quantecon/returns nothingFrom the July 2026 technical-debt audit (AI-assisted; claims verified against
28d4b3bon 2026-07-25).