Skip to content

Support array_like low/high bounds for randint - #168

Open
vlad-perevezentsev wants to merge 9 commits into
masterfrom
randint_array_like_bounds
Open

Support array_like low/high bounds for randint#168
vlad-perevezentsev wants to merge 9 commits into
masterfrom
randint_array_like_bounds

Conversation

@vlad-perevezentsev

@vlad-perevezentsev vlad-perevezentsev commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

This PR adds array_like low/high support to randint to align with numpy.random.RandomState.randint

It adds new C routines irk_rand_<type>_broadcast for all 9 integer dtypes, drawing per-element integers over [low, high) with Lemire's multiply-shift (the same method as NumPy).
An earlier masked-rejection version benchmarked slower so it was replaced.

A mulhi helper returns the high half of the product because Lemire computes word × range which overflows the type so the 64-bit case uses a 32-bit schoolbook multiply.

It also groups all randint tests into TestRandint class and adds new ones.

@ndgrigorian

Copy link
Copy Markdown
Collaborator

@vlad-perevezentsev
We will want to apply to randint_untyped as well

Comment thread mkl_random/mklrand.pyx

Generate a 2 by 4 array using broadcasting with dtype of uint8

>>> mkl_random.randint([1, 3, 5, 7], [[10], [20]], dtype=numpy.uint8)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
>>> mkl_random.randint([1, 3, 5, 7], [[10], [20]], dtype=numpy.uint8)
>>> mkl_random.randint([1, 3, 5, 7], [[10], [20]], dtype=np.uint8)

aligns with rest of the file

Comment thread mkl_random/mklrand.pyx
Comment on lines +2308 to +2314
max_high = int(np.max(high_b))
if int(np.min(low_b)) < lowbnd:
raise ValueError(f"low is out of bounds for {_dtype.name}")
if max_high > highbnd:
raise ValueError(f"high is out of bounds for {_dtype.name}")
if np.any(low_b >= high_b):
raise ValueError("low >= high")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

NumPy approaches this validation a bit differently:
https://github.com/numpy/numpy/blob/2f8bc80b22478d35627856679d0eaf876688ca39/numpy/random/_bounded_integers.pyx.in

I wonder if it's more efficient, since it uses the original arrays instead of broadcast arrays

@ndgrigorian

Copy link
Copy Markdown
Collaborator

@vlad-perevezentsev
Edge case issue:

In [5]: np.random.randint([3], [9], size=())
Out[5]: array(6)

In [6]: rand.randint([3], [9], size=())
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[6], line 1
----> 1 rand.randint([3], [9], size=())

File mkl_random/mklrand.pyx:2455, in mkl_random.mklrand._MKLRandomState.randint()
-> 2455 'Could not get source, probably due dynamically evaluated source code.'

File mkl_random/mklrand.pyx:2302, in mkl_random.mklrand._MKLRandomState._randint_broadcast()
-> 2302 'Could not get source, probably due dynamically evaluated source code.'

File ~/miniforge3/envs/dpctl_dev/lib/python3.14t/site-packages/numpy/lib/_stride_tricks_impl.py:443, in broadcast_to(array, shape, subok)
    400 @array_function_dispatch(_broadcast_to_dispatcher, module='numpy')
    401 def broadcast_to(array, shape, subok=False):
    402     """Broadcast an array to a new shape.
    403
    404     Parameters
   (...)    441            [1, 2, 3]])
    442     """
--> 443     return _broadcast_to(array, shape, subok=subok, readonly=True)

File ~/miniforge3/envs/dpctl_dev/lib/python3.14t/site-packages/numpy/lib/_stride_tricks_impl.py:377, in _broadcast_to(array, shape, subok, readonly)
    375 array = np.array(array, copy=None, subok=subok)
    376 if not shape and array.shape:
--> 377     raise ValueError('cannot broadcast a non-scalar to a scalar array')
    378 if any(size < 0 for size in shape):
    379     raise ValueError('all elements of broadcast shape must be non-'
    380                      'negative')

ValueError: cannot broadcast a non-scalar to a scalar array

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants