Skip to content

geometric() returns failures before the first success (k >= 0) instead of trials (k >= 1) #182

Description

@vchamarthi

Affects: mkl_random.MKLRandomState.geometric, mkl_random.interfaces.numpy_random.geometric, and numpy.random.geometric after mkl_random.patch_numpy_random()

Issue

Every sample from geometric(p) is one less than it should be.

NumPy defines the geometric distribution on k = 1, 2, ... with f(k) = (1 - p)^(k-1) p. mkl_random's docstring (mklrand.pyx:6018-6024) states the same, and the NumPy interface refers users to numpy.random.geometric.

irk_geometric_vec (src/mkl_distributions.cpp:1302) returns the output of viRngGeometric unchanged. oneMKL defines that output as the number of failures before the first success: P(X = k) = p (1 - p)^k for k in {0, 1, 2, ...}. The p == 1.0 branch writes 0 where NumPy returns 1.

test_randomdsit_geometric (tests/test_random.py:864) has 0 in its expected values, so the suite passes with this behaviour.

import numpy as np
import mkl_random

x = mkl_random.interfaces.numpy_random.RandomState(1).geometric(0.3, size=10**7)
print(x.min(), x.mean())                                             # 0 2.3330
print(np.random.RandomState(1).geometric(0.3, size=10**7).mean())   # 3.3331

mkl_random.patch_numpy_random()
print(np.random.geometric(0.3, size=10**6).min())                    # 0

print(mkl_random.interfaces.numpy_random.RandomState(0).geometric(1.0, 3))  # [0 0 0]; NumPy: [1 1 1]

How found

Chi-square goodness-of-fit against the exact k >= 1 pmf, with 10^7 draws and seeds 1, 2 and 3. As a control, an exact inverse-CDF sampler under the same test gives z ~ N(0, 1).

p exact mean mkl_random mean mkl_random z mkl_random + 1, z NumPy Generator z
0.01 100.000 98.975 20.7, 20.9, 23.1 -1.4, -1.3, 0.8 -0.4, -0.7, 0.5
0.3 3.3333 2.3325 ~1.0e5 -0.9, 0.3, 0.6 -1.7, -0.3, -0.4
0.5 2.0000 0.9995 ~4.0e5 -1.0, 0.4, -1.7 0.7, -0.5, -0.5
0.9 1.1111 0.1109 ~2.3e6 0.3, 0.3, -0.2 2.2, 1.3, 0.2

The unshifted output fits the k >= 0 pmf (z between -1.4 and 0.3). The output is bit-identical to calling viRngGeometric directly. It is also identical with oneMKL 2023.2, 2024.2.2, 2025.0.1, 2025.3.1, 2026.0 and 2026.1, and on all six BRNGs (MT19937, SFMT19937, MT2203, MCG59, PHILOX4X32X10, ARS5).

Environment: mkl_random 1.5.0 (py313h581662c_0, Intel channel), oneMKL 2026.1.0, NumPy 2.4.3, Linux x86-64.

Impact

With the patch enabled, code calling numpy.random.geometric gets different results: the mean is 1 lower, and 0 appears with probability p although NumPy never returns it. Scalar p, array p and p = 1 are all affected.

Possible fix

  • Add 1 to each element after viRngGeometric in irk_geometric_vec, and write 1 in the p == 1.0 branch.
  • Update the expected values in test_randomdsit_geometric. Add a test that geometric(p).min() >= 1 and that the mean is within tolerance of 1/p.
  • Check the other discrete wrappers for the same oneMKL-vs-NumPy support mismatch.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions