Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

* Added support for boolean input in `dpnp.positive` and the `+` operator of `dpnp.ndarray` [#3128](https://github.com/IntelPython/dpnp/pull/3128)
* Added `dpnp.top_k` to return the `k` largest or smallest elements and their indices along an axis, with a `sorted` keyword to return them in sorted order [#3133](https://github.com/IntelPython/dpnp/pull/3133)
* Added the `dpnp.random.Generator` API with `default_rng`, `BitGenerator`, `SeedSequence` and the `Philox4x3210` bit generator, sampling on the device with the oneMKL device RNG API [#3157](https://github.com/IntelPython/dpnp/pull/3157)

### Changed

Expand Down
6 changes: 6 additions & 0 deletions doc/known_words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ ary
backend
bandlimited
bincount
BitGenerator
bitwise
Blackman
boolean
Expand Down Expand Up @@ -77,21 +78,25 @@ Nyquist
oneAPI
ord
orthonormal
Philox

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Philox belongs after Penrose.

On the 41 words from my review: that was the number of new warning lines from the spelling step, not of words. At this head the step reports 10 lines more than master. The only new word is rng, from the dpnp.random.default_rng page; oneMKL, sycl and reproducibility are flagged on master already. So rng is the only word left to add.

radix
Penrose
Polyutils
pre
prepend
prepended
prepending
reentrant
representable
resampling
runtimes
scikit
se
SeedSequence
signbit
signum
sinc
strided
subarray
subarrays
subclasses
Expand All @@ -118,4 +123,5 @@ Vetterline
von
Weibull
whitespace
ziggurat
Zipf
113 changes: 106 additions & 7 deletions doc/reference/random.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,112 @@
Random sampling (:mod:`dpnp.random`)
====================================

.. hint:: `NumPy API Reference: Random sampling <https://numpy.org/doc/stable/reference/random/index.html>`_

DPNP draws random numbers on the SYCL device, with the counter-based
Philox4x32x10 generator of the oneMKL device RNG API. It provides the
:class:`Generator` API of NumPy, recommended for new code. The legacy
:class:`RandomState` API and the module-level functions still use the
host-side oneMKL RNG.

.. code-block:: python

import dpnp

rng = dpnp.random.default_rng(seed=42)
x = rng.standard_normal((3, 4)) # on the default device
y = rng.random(5, usm_type="shared")

rng = dpnp.random.default_rng(42, device="cpu")

A generator draws all its samples on one SYCL queue, chosen with the
``device`` or ``sycl_queue`` keyword when it is created. All samples are
:class:`dpnp.ndarray`, zero-dimensional when ``size`` is ``None``.

Differences from NumPy
----------------------

* The streams differ from NumPy's. For a given seed, output size and data
type, the raw bits and the uniform floats are the same on every device,
while the normal samples agree to a few ULP.
* The values depend on the output size: a draw is not a prefix of a
larger one, and two draws of ``n`` values differ from one draw of
``2 * n``. Code splitting its draws into chunks gets other values.
* The floating-point distributions take a ``dtype`` keyword, as in CuPy,
which defaults to the default floating-point type of the device, so on a
device without double precision the same seed gives other values by
default.
* All methods take a ``usm_type`` keyword. With ``usm_type=None``, the
default, the samples take the USM type of the array parameters, or
``"device"`` when there are none.
* Parameters on the host (Python scalars, lists and NumPy arrays) are
checked as in NumPy. Parameters in :class:`dpnp.ndarray` are
not, since that would wait for the device.
Invalid ones give undefined values, such as NaN.
* Generators and bit generators cannot be pickled or copied, and the bit

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

copy.copy works; only pickle and copy.deepcopy fail.
copy.copy of a Generator returns one that shares the bit generator, and copy.copy of a Philox4x3210 an independent one in the same state, as in NumPy (it also shares the original's lock, which NumPy's copy doesn't). pickle.dumps and copy.deepcopy of either raise TypeError. _generator.py:202 says the same as this line.

Suggestion: "cannot be pickled or deep-copied".

generators have no ``advance`` or ``jumped`` method.

Random Generator

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this heading and the legacy "Random generator" (line 181) differ only by one capital letter.
The page's table of contents shows "Random Generator" for the new API and "Random generator" for RandomState under "Legacy random generation". The other headings on this page are in sentence case ("Accessing the bit generator and spawning", "Bit generators", "Legacy random generation"); "Random Generator" follows NumPy's page title.

Suggestion: rename one of them, e.g. the legacy one to "RandomState", so the two sections are easy to tell apart.

----------------

.. autosummary::
:toctree: generated/
:nosignatures:

default_rng
Generator

Accessing the bit generator and spawning
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. autosummary::
:toctree: generated/
:nosignatures:

Generator.bit_generator
Generator.spawn

Simple random data
^^^^^^^^^^^^^^^^^^

.. autosummary::
:toctree: generated/
:nosignatures:

Generator.random

Distributions
^^^^^^^^^^^^^

.. autosummary::
:toctree: generated/
:nosignatures:

Generator.normal
Generator.standard_normal
Generator.uniform

Bit generators
--------------

.. autosummary::
:toctree: generated/
:nosignatures:

BitGenerator
BitGenerator.random_raw
BitGenerator.spawn
BitGenerator.state

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some documented properties have no page.
dpnp's class pages don't list their members (the RandomState page doesn't either), so a member is rendered only when this file lists it. Generator.sycl_queue and Generator.sycl_device, and BitGenerator.lock, BitGenerator.seed_seq, BitGenerator.sycl_queue and BitGenerator.sycl_device have docstrings but aren't listed, so they appear nowhere in the rendered docs. lock matters most: its docstring is the only place that says the lock is reentrant and how to use it. ndarray.rst:73-75 lists ndarray.sycl_device, ndarray.sycl_queue and ndarray.usm_type.

Suggestion: add them to the Generator and BitGenerator autosummary lists.

Philox4x3210
SeedSequence

Legacy random generation
------------------------

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

Simple random data
------------------
^^^^^^^^^^^^^^^^^^

.. autosummary::
:toctree: generated/
Expand All @@ -23,9 +125,8 @@ Simple random data
choice
bytes


Permutations
------------
^^^^^^^^^^^^

.. autosummary::
:toctree: generated/
Expand All @@ -34,9 +135,8 @@ Permutations
shuffle
permutation


Distributions
-------------
^^^^^^^^^^^^^

.. autosummary::
:toctree: generated/
Expand Down Expand Up @@ -78,9 +178,8 @@ Distributions
weibull
zipf


Random generator
----------------
^^^^^^^^^^^^^^^^

.. autosummary::
:toctree: generated/
Expand Down
2 changes: 2 additions & 0 deletions dpnp/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- `dpnp_algo/dpnp_elementwise_common.py`: ufunc wrapper classes (`DPNPUnaryFunc`, `DPNPBinaryFunc`, two-output and specialised variants)
- `dpnp_utils/`: shared helpers (`get_usm_allocations`, `map_dtype_to_device`, reduction wrappers)
- `linalg/`, `fft/`, `random/`, `scipy/`, `lib/`: subpackages, each exporting through its own `__init__.py`
- `random/`: `_bit_generator.py` (`BitGenerator`, `Philox4x3210`; `SeedSequence` is NumPy's) and `_generator.py` (`Generator`, which implements every distribution). The kernels are in `dpnp.libtensor._rng_impl`.
- 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`).

## Adding a Public Function
Expand All @@ -20,6 +21,7 @@
- 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.
- Use `dpnp.default_float_type(...)` or `map_dtype_to_device(...)` for the default float type.
- Deprecated ufuncs use `DPNPDeprecatedUnaryFunc(..., deprecated_msg=...)`.
- 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` or `_float_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`).
- Unsupported NumPy keywords raise `NotImplementedError` (`dpnp.check_limitations`) and are listed under `Limitations` in the docstring.
- 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`.
- `dpnp.tensor` replaces `dpctl.tensor`, which no longer exists here; `SyclQueue`, `SyclDevice`, `dpctl.memory` and `SequentialOrderManager` still come from `dpctl`.
Expand Down
1 change: 1 addition & 0 deletions dpnp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ add_subdirectory(libtensor/source)
add_subdirectory(libtensor/extensions/blas)
add_subdirectory(libtensor/extensions/fft)
add_subdirectory(libtensor/extensions/lapack)
add_subdirectory(libtensor/extensions/rng)
add_subdirectory(libtensor/extensions/vm)
add_subdirectory(libtensor/extensions/sparse)

Expand Down
5 changes: 3 additions & 2 deletions dpnp/libtensor/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
## Layout
- `include/kernels/`: header-only SYCL kernels in namespaces under `dpnp::tensor::kernels`; `include/utils/`: type dispatch, type, offset and memory-overlap helpers (`type_dispatch.hpp`, `type_dispatch_building.hpp`, `type_utils.hpp`, `memory_overlap.hpp`, ...); `include/ext/`: dispatch helpers (`init_dispatch_vector`, `init_dispatch_table`, `DispatchTable`) and validation helpers (`validation_utils.hpp`). The headers ship with dpnp; `dpnp.get_include()` and `python -m dpnp --tensor-include-dir` return `include/`.
- `source/`: bindings built by `source/CMakeLists.txt` into `_tensor_impl`, `_tensor_elementwise_impl`, `_tensor_reductions_impl`, `_tensor_sorting_impl`, `_tensor_accumulation_impl`, `_tensor_linalg_impl`, `_statistics_impl` and `_window_impl`. The functions dpnp adds beyond the Array API are listed in the `_dpnp_*_sources` variables there and compiled into the same modules.
- `extensions/{blas,fft,lapack,sparse,vm}/`: oneMKL/oneMath extensions, one module `_<name>_impl` per directory.
- `extensions/{blas,fft,lapack,rng,sparse,vm}/`: oneMKL/oneMath extensions, one module `_<name>_impl` per directory.
- Every module installs into `dpnp/libtensor` and is imported as `dpnp.libtensor._<name>_impl`. A new kernel goes into `source/` and an existing module; only code that needs oneMKL gets a new extension.

## Adding a ufunc Kernel (model: `fabs`)
Expand All @@ -21,8 +21,9 @@ A new extension's `CMakeLists.txt` lists its sources and calls `build_dpnp_pybin
- Validate inputs before launching: queue compatibility, contiguity, writability, memory overlap (including the same logical tensor), dtype and ndim. Reuse the helpers neighbouring files use: `check_queue`, `check_c_contig`, `check_no_overlap`, `check_writable` from `include/ext/validation_utils.hpp` (`putmask`, `statistics`, `isclose`, `interpolate`), or `dpnp::utils::queues_are_compatible`, `dpnp::tensor::overlap::MemoryOverlap` and `dpnp::tensor::validation::CheckWritable` (extensions and the Array API functions). Don't silently accept strided input.
- Throw `py::value_error` for bad arguments and `std::runtime_error` for internal failures; catch oneMKL and SYCL exceptions and rethrow them.
- Guard double, complex<double> and half with `dpnp::tensor::type_utils::validate_type_for_device<T>(q)`; don't use double literals or casts in kernels that must run without fp64.
- `blas`, `fft`, `lapack` and `sparse` build against either oneMKL or oneMath (`USE_ONEMATH`); `vm` has kernels only with oneMKL: with oneMath it builds empty and `_is_available()` returns `False`.
- `blas`, `fft`, `lapack` and `sparse` build against either oneMKL or oneMath (`USE_ONEMATH`); `vm` has kernels only with oneMKL: with oneMath it builds empty and `_is_available()` returns `False`; `rng` uses the header-only oneMKL device RNG API and links no oneMKL library.
- Release the GIL around blocking oneMKL calls (`py::gil_scoped_release`).
- `rng` draws depend only on the engine state and the number of elements, never on the device or the layout chosen for it (`extensions/rng/kernels/sample.hpp`): raw bits and uniform floats are bit-identical across devices, samplers using transcendental functions agree to a few ULP. Each engine's `items_per_work_item` (`kernels/engines.hpp`) is part of its stream definition.
- An extension whose kernels may be launched over more than `INT_MAX` work-items passes `LARGE_RANGES` to `build_dpnp_pybind11_ext`, which adds `-fno-sycl-id-queries-fit-in-int` at compile and link time (DPC++ 2026.2 and later need it at link time). `source/CMakeLists.txt` and `dpnp_backend_c` pass it to both `target_compile_options` and `target_link_options`.
- clang-format 23 formats C++ (`.clang-format`: LLVM base, 4-space indent, 80 columns); gersemi formats CMake at 88 columns, except `dpnp/backend/cmake/Modules/`.

Expand Down
50 changes: 50 additions & 0 deletions dpnp/libtensor/extensions/rng/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
# *****************************************************************************
# Copyright (c) 2026, Intel Corporation
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# - Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# - Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# - Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.
# *****************************************************************************

set(python_module_name _rng_impl)
set(_module_src
${CMAKE_CURRENT_SOURCE_DIR}/normal.cpp
${CMAKE_CURRENT_SOURCE_DIR}/rng_py.cpp
${CMAKE_CURRENT_SOURCE_DIR}/sample.cpp
${CMAKE_CURRENT_SOURCE_DIR}/uniform.cpp
)

# a launch takes one work-item per 8 elements, which can exceed INT_MAX
build_dpnp_pybind11_ext(
${python_module_name}
SOURCES ${_module_src}
LARGE_RANGES
)

# oneMKL device RNG API is header-only, so no oneMKL library is linked
target_include_directories(
${python_module_name}
SYSTEM
PRIVATE $<TARGET_PROPERTY:MKL::MKL_SYCL,INTERFACE_INCLUDE_DIRECTORIES>
)
Loading
Loading