Skip to content

Commit 0232b4c

Browse files
committed
Rebuild RandomState and dpnp.random functions on the device RNG
RandomState and the module functions now delegate to the Generator samplers, keeping NumPy's legacy checks and messages, also for parameters on the device. The module keeps one RandomState per queue, and adds get_state, set_state, get_bit_generator and set_bit_generator. Enable the CuPy random tests the device RNG supports.
1 parent ed4d347 commit 0232b4c

20 files changed

Lines changed: 2808 additions & 4027 deletions

‎CHANGELOG.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ This release drops support for Python 3.10, making Python 3.11 the minimum requi
2020
* Added the multivariate distributions, `permutation`, `shuffle`, `permuted`, `choice` and `bytes` to `dpnp.random.Generator` [#3160](https://github.com/IntelPython/dpnp/pull/3160)
2121
* Added the `dpnp.random.PCG64DXSM` bit generator, which matches `numpy.random.PCG64DXSM` for raw values, `random`, `uniform` and `bytes` [#3161](https://github.com/IntelPython/dpnp/pull/3161)
2222
* Added the `dpnp.random.MRG32k3a` bit generator [#3161](https://github.com/IntelPython/dpnp/pull/3161)
23+
* Added `dpnp.random.get_state`, `dpnp.random.set_state`, `dpnp.random.get_bit_generator` and `dpnp.random.set_bit_generator`, and support for a bit generator as the seed of `dpnp.random.RandomState` [#3162](https://github.com/IntelPython/dpnp/pull/3162)
2324

2425
### Changed
2526

@@ -32,6 +33,7 @@ This release drops support for Python 3.10, making Python 3.11 the minimum requi
3233
* Reduced the overhead of `dpnp.ndarray.flags` and `dpnp.tensor.usm_ndarray.flags` [#3168](https://github.com/IntelPython/dpnp/pull/3168)
3334
* Changed `dpnp.linalg.LinAlgError` to derive from `numpy.linalg.LinAlgError`, so `except numpy.linalg.LinAlgError` also catches errors raised by dpnp linear algebra functions [#3184](https://github.com/IntelPython/dpnp/pull/3184)
3435
* Bumped the minimum required `dpctl` version to `0.24.0` [#3169](https://github.com/IntelPython/dpnp/pull/3169)
36+
* Reimplemented `dpnp.random.RandomState` and the `dpnp.random` functions on device kernels, drawing on the queue of the generator. `device`, `usm_type` and `sycl_queue` are now keyword-only, `randint(dtype=int)` returns `int64` and `shuffle` requires a `dpnp.ndarray` [#3162](https://github.com/IntelPython/dpnp/pull/3162)
3537

3638
### Deprecated
3739

‎doc/reference/random.rst‎

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ DPNP draws random numbers on the SYCL device, by default with the
99
counter-based Philox4x32x10 generator of the oneMKL device RNG API, or with
1010
NumPy's PCG64DXSM generator or the MRG32k3a generator of the oneMKL device
1111
RNG API. A oneMath build takes Philox4x32x10 and MRG32k3a from oneMath's
12-
device RNG API instead. It provides the :class:`Generator` API of NumPy,
13-
recommended for new code. The legacy :class:`RandomState` API and the
14-
module-level functions still use the host-side oneMKL RNG.
12+
device RNG API instead. It provides both NumPy random APIs: the
13+
:class:`Generator` API recommended for new code, and the legacy
14+
:class:`RandomState` API with the module-level functions built on it.
1515

1616
.. code-block:: python
1717
@@ -27,6 +27,8 @@ A generator draws all its samples on one SYCL queue, chosen with the
2727
``device`` or ``sycl_queue`` keyword when it is created. All samples are
2828
:class:`dpnp.ndarray`, zero-dimensional when ``size`` is ``None``.
2929

30+
.. _random-differences:
31+
3032
Differences from NumPy
3133
----------------------
3234

@@ -52,8 +54,8 @@ Differences from NumPy
5254
default, the samples take the USM type of the array parameters, or
5355
``"device"`` when there are none.
5456
* Parameters on the host (Python scalars, lists and NumPy arrays) are
55-
checked as in NumPy. Parameters in :class:`dpnp.ndarray` are
56-
not, since that would wait for the device.
57+
checked as in NumPy. In :class:`Generator`, parameters in
58+
:class:`dpnp.ndarray` are not, since that would wait for the device.
5759
Invalid ones give undefined values, such as NaN or the smallest ``int64``,
5860
but the kernels handle them safely: they never loop without end or access
5961
memory out of bounds. Floating-point arrays given for integer parameters
@@ -72,9 +74,15 @@ Differences from NumPy
7274
* ``zipf`` follows the Zipf distribution truncated to the largest ``int64``.
7375
NumPy's rejection test rounds to accepting every sample above ``2**53``,
7476
which makes its tail too heavy for ``a`` close to 1.
75-
* ``hypergeometric`` requires ``ngood`` and ``nbad`` below ``10**9``.
76-
* :meth:`Generator.shuffle` only accepts a :class:`dpnp.ndarray`, as in
77-
CuPy.
77+
* :meth:`RandomState.randint` and :func:`randint` return ``int64`` values by
78+
default, as do :meth:`RandomState.random_integers`,
79+
:func:`random_integers` and :meth:`RandomState.tomaxint`, whose values
80+
reach the largest ``int64``. NumPy returns a C ``long`` instead, which is
81+
``int32`` on Windows.
82+
* ``hypergeometric`` requires ``ngood`` and ``nbad`` below ``10**9``, also in
83+
:class:`RandomState`.
84+
* :meth:`Generator.shuffle` and :func:`shuffle` only accept a
85+
:class:`dpnp.ndarray`, as in CuPy.
7886
* Generators and bit generators cannot be pickled or deep-copied,
7987
and :class:`Philox4x3210` and :class:`MRG32k3a` have no ``advance`` or
8088
``jumped`` method.
@@ -195,6 +203,24 @@ Legacy random generation
195203

196204
.. hint:: `NumPy API Reference: Legacy random generation <https://numpy.org/doc/stable/reference/random/legacy.html>`_
197205

206+
:class:`RandomState` keeps the parameter checks of NumPy's legacy one, also
207+
for parameters in :class:`dpnp.ndarray`, as CuPy's does, so that a call with
208+
such parameters waits for the device to check them. The module-level
209+
functions use one :class:`RandomState` per SYCL queue: the
210+
queue given by the ``device`` or ``sycl_queue`` keyword, or else the queue of
211+
a :class:`dpnp.ndarray` parameter, or else the default queue.
212+
213+
.. autosummary::
214+
:toctree: generated/
215+
:nosignatures:
216+
217+
RandomState
218+
seed
219+
get_state
220+
set_state
221+
get_bit_generator
222+
set_bit_generator
223+
198224
Simple random data
199225
^^^^^^^^^^^^^^^^^^
200226

@@ -265,15 +291,3 @@ Distributions
265291
wald
266292
weibull
267293
zipf
268-
269-
RandomState
270-
^^^^^^^^^^^
271-
272-
.. autosummary::
273-
:toctree: generated/
274-
:nosignatures:
275-
276-
RandomState
277-
seed
278-
get_random_state
279-
set_random_state

‎dpnp/AGENTS.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
- `dpnp_algo/dpnp_elementwise_common.py`: ufunc wrapper classes (`DPNPUnaryFunc`, `DPNPBinaryFunc`, two-output and specialised variants)
77
- `dpnp_utils/`: shared helpers (`get_usm_allocations`, `map_dtype_to_device`, reduction wrappers)
88
- `linalg/`, `fft/`, `random/`, `scipy/`, `lib/`: subpackages, each exporting through its own `__init__.py`
9-
- `random/`: `_bit_generator.py` (`BitGenerator`, `Philox4x3210`, `PCG64DXSM`, `MRG32k3a`; `SeedSequence` is NumPy's) and `_generator.py` (`Generator`, which implements every distribution). The kernels are in `dpnp.libtensor._rng_impl`.
9+
- `random/`: `_bit_generator.py` (`BitGenerator`, `Philox4x3210`, `PCG64DXSM`, `MRG32k3a`; `SeedSequence` is NumPy's), `_generator.py` (`Generator`, which implements every distribution), `dpnp_random_state.py` (`RandomState`, the same samplers with NumPy's legacy checks) and `dpnp_iface_random.py` (module functions, one `RandomState` per queue). The kernels are in `dpnp.libtensor._rng_impl`.
1010
- Legacy, don't extend: the Cython `dpnp_algo` path and `dpnp_backend_c` (still used by `dpnp.partition` and `dpnp.random`), and the NumPy fallback `call_origin`. New kernels go into `dpnp/libtensor` (see `dpnp/libtensor/AGENTS.md`).
1111

1212
## Adding a Public Function
@@ -21,7 +21,7 @@
2121
- Extension calls are ordered with `_manager = dpctl.utils.SequentialOrderManager[exec_q]`: pass `depends=_manager.submitted_events`, then `_manager.add_event_pair(ht_ev, ev)`. Don't add blocking waits.
2222
- Use `dpnp.default_float_type(...)` or `map_dtype_to_device(...)` for the default float type.
2323
- Deprecated ufuncs use `DPNPDeprecatedUnaryFunc(..., deprecated_msg=...)`.
24-
- A `dpnp.random` distribution is a `Generator` method: it calls `self._check_queues(...)` on its array parameters (raises `ExecutionPlacementError`), checks host parameters as NumPy does, and draws through `_cont`, `_float_sample` or `_discrete_sample` into `BitGenerator._fill`. Parameters on the device are not checked, as the check waits for the device. With `usm_type=None`, the output and the temporaries take the coerced USM type of the array parameters (`Generator._sample`).
24+
- A `dpnp.random` distribution is a `Generator` method: it calls `self._check_queues(...)` on its array parameters (raises `ExecutionPlacementError`), checks host parameters as NumPy does, and draws through `_cont`, `_float_sample` or `_discrete_sample` into `BitGenerator._fill`. `RandomState` and the module function delegate to it. Parameters on the device are checked only by `RandomState` (`_check_device`), as the check waits for the device. With `usm_type=None`, the output and the temporaries take the coerced USM type of the array parameters (`Generator._sample`).
2525
- Unsupported NumPy keywords raise `NotImplementedError` (`dpnp.check_limitations`) and are listed under `Limitations` in the docstring.
2626
- The Array API spec version is set in three places that change together: `__array_api_version__` in `dpnp/tensor/_array_api.py`, `ARRAY_API_TESTS_VERSION` in `.github/workflows/conda-package.yml` and `doc/reference/array_api.rst` (plus the version test in `dpnp/tests/test_ndarray.py`). Inspection utilities come from `__array_namespace_info__` in `dpnp_array_api_info.py`.
2727
- `dpnp.tensor` replaces `dpctl.tensor`, which no longer exists here; `SyclQueue`, `SyclDevice`, `dpctl.memory` and `SequentialOrderManager` still come from `dpctl`.

‎dpnp/random/__init__.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
f,
4646
gamma,
4747
geometric,
48+
get_bit_generator,
49+
get_state,
4850
gumbel,
4951
hypergeometric,
5052
laplace,
@@ -71,6 +73,8 @@
7173
rayleigh,
7274
sample,
7375
seed,
76+
set_bit_generator,
77+
set_state,
7478
shuffle,
7579
standard_cauchy,
7680
standard_exponential,
@@ -104,6 +108,8 @@
104108
"f",
105109
"gamma",
106110
"geometric",
111+
"get_bit_generator",
112+
"get_state",
107113
"gumbel",
108114
"hypergeometric",
109115
"laplace",
@@ -131,6 +137,8 @@
131137
"sample",
132138
"shuffle",
133139
"seed",
140+
"set_bit_generator",
141+
"set_state",
134142
"standard_cauchy",
135143
"standard_exponential",
136144
"standard_gamma",

0 commit comments

Comments
 (0)