Skip to content

Commit 2b71b91

Browse files
committed
fix pr comments and suggestions
1 parent d34296b commit 2b71b91

3 files changed

Lines changed: 87 additions & 17 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77
# [dev] (MM/DD/YYYY)
88

99
### Added
10-
* Added tests for the array-valued parameter paths of the location and scale distributions [gh-171](https://github.com/IntelPython/mkl_random/pull/171)
1110
* Added support for `array_like` (broadcastable) `low`/`high` bounds in `randint` [gh-168](https://github.com/IntelPython/mkl_random/pull/168)
1211

1312
### Changed
14-
* Sped up `normal`, `uniform`, `exponential`, `laplace`, `gumbel`, `logistic`, `rayleigh` and `lognormal` for array-valued parameters [gh-171](https://github.com/IntelPython/mkl_random/pull/171)
15-
* The random streams for the array-valued-parameter paths of the distributions above have changed: with a fixed seed these now produce different (but equally valid) samples. Scalar-parameter paths are unaffected. [gh-171](https://github.com/IntelPython/mkl_random/pull/171)
13+
* Sped up `normal`, `uniform`, `exponential`, `laplace`, `gumbel`, `logistic`, `rayleigh` and `lognormal` for array-valued parameters. Seeded results change for these array paths; scalar paths are unchanged [gh-171](https://github.com/IntelPython/mkl_random/pull/171)
14+
* Array parameters for these distributions must broadcast to the requested `size` without adding dimensions; previously accepted mismatches now raise `ValueError` [gh-171](https://github.com/IntelPython/mkl_random/pull/171)
15+
* `uniform` with array-valued bounds may return `high` due to floating-point rounding [gh-171](https://github.com/IntelPython/mkl_random/pull/171)
1616
* Pinned Cython in the Coverity Scan workflow so generated code stays stable between scans, and added `coverity/README.md` documenting the known Cython-boilerplate false positives and the scan review checklist [gh-164](https://github.com/IntelPython/mkl_random/pull/164)
1717

1818
### Fixed

‎mkl_random/mklrand.pyx‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -887,7 +887,8 @@ cdef object vec_lognormal_array(
887887
"""Draw lognormals with array-valued parameters, one call per request.
888888
889889
Uses the normal fill: the parameters sit inside the exponential, so no
890-
affine step applies to a standardised lognormal, but exp(mean + sigma * z) does.
890+
affine step applies to a standardised lognormal,
891+
but exp(mean + sigma * z) does.
891892
"""
892893
cdef object array
893894

@@ -2840,16 +2841,19 @@ cdef class _MKLRandomState:
28402841
Samples are uniformly distributed over the half-open interval
28412842
``[low, high)`` (includes low, but excludes high). In other words,
28422843
any value within the given interval is equally likely to be drawn
2843-
by `uniform`.
2844+
by `uniform`. With array-valued bounds, floating-point rounding
2845+
may include the upper boundary in the returned samples.
28442846
28452847
Parameters
28462848
----------
28472849
low : float, optional
28482850
Lower boundary of the output interval. All values generated will be
28492851
greater than or equal to low. The default value is 0.
28502852
high : float
2851-
Upper boundary of the output interval. All values generated will be
2852-
less than high. The default value is 1.0.
2853+
Upper boundary of the output interval. With array-valued bounds,
2854+
high may be included due to floating-point rounding in
2855+
``low + (high - low) * U``, where ``U`` is drawn from ``[0, 1)``.
2856+
The default value is 1.0.
28532857
size : int or tuple of ints, optional
28542858
Output shape. If the given shape is, e.g., ``(m, n, k)``, then
28552859
``m * n * k`` samples are drawn. Default is None, in which case a

‎mkl_random/tests/test_random.py‎

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,7 +1327,7 @@ def test_two_param_array_matches_scalar(name, draw, pa, pb):
13271327
np.testing.assert_allclose(
13281328
arrayed,
13291329
scalar,
1330-
rtol=1e-9,
1330+
rtol=1e-8 if name == "lognormal" else 1e-9,
13311331
atol=1e-9 * float(np.std(scalar)),
13321332
err_msg=f"{name}: array-parameter path disagrees with scalar path",
13331333
)
@@ -1337,14 +1337,54 @@ def test_two_param_array_matches_scalar(name, draw, pa, pb):
13371337
"name,draw,pa,pb", _LOC_SCALE_DISTS, ids=[d[0] for d in _LOC_SCALE_DISTS]
13381338
)
13391339
def test_two_param_array_applies_per_element(name, draw, pa, pb):
1340-
# A scale sweep must widen the spread across the result.
1341-
n = 60000
1342-
lo = np.full(n, pa)
1343-
hi = np.linspace(pb, pb * 4.0, n)
1344-
out = draw(rnd.MKLRandomState(99), lo, hi, None)
1345-
first, last = out[: n // 4], out[-n // 4 :]
1346-
assert np.std(last) > np.std(first), (
1347-
f"{name}: per-element parameters do not appear to be applied"
1340+
loc = np.linspace(pa, pa + 2.0, 3)[:, None]
1341+
scale = np.linspace(pb, pb * 4.0, 4)
1342+
shape = (3, 4)
1343+
reference = rnd.MKLRandomState(99)
1344+
if name == "lognormal":
1345+
standard = reference.standard_normal(shape)
1346+
expected = np.exp(loc + scale * standard)
1347+
else:
1348+
standard = draw(reference, 0.0, 1.0, shape)
1349+
width = scale - loc if name == "uniform" else scale
1350+
expected = loc + width * standard
1351+
out = draw(rnd.MKLRandomState(99), loc, scale, None)
1352+
assert out.shape == shape
1353+
np.testing.assert_allclose(
1354+
out,
1355+
expected,
1356+
rtol=1e-12,
1357+
atol=1e-12,
1358+
err_msg=f"{name}: per-element parameters are not applied correctly",
1359+
)
1360+
1361+
1362+
@pytest.mark.parametrize(
1363+
"name,method,normal_method",
1364+
[
1365+
("normal", "ICDF", "ICDF"),
1366+
("normal", "BoxMuller", "BoxMuller"),
1367+
("normal", "BoxMuller2", "BoxMuller2"),
1368+
("lognormal", "ICDF", "ICDF"),
1369+
("lognormal", "BoxMuller", "BoxMuller2"),
1370+
],
1371+
)
1372+
@pytest.mark.parametrize("size", [None, (2, 3), (3, 3)])
1373+
def test_normal_family_array_methods(name, method, normal_method, size):
1374+
loc = np.array([-0.5, 0.0, 0.5])
1375+
scale = np.array([0.5, 1.0, 1.5])
1376+
shape = loc.shape if size is None else size
1377+
reference = rnd.MKLRandomState(1234)
1378+
state = rnd.MKLRandomState(1234)
1379+
standard = reference.standard_normal(shape, method=normal_method)
1380+
expected = loc + scale * standard
1381+
if name == "lognormal":
1382+
expected = np.exp(expected)
1383+
actual = getattr(state, name)(loc, scale, size, method=method)
1384+
assert actual.shape == shape
1385+
np.testing.assert_allclose(actual, expected, rtol=1e-12, atol=1e-12)
1386+
np.testing.assert_array_equal(
1387+
state.random_sample(32), reference.random_sample(32)
13481388
)
13491389

13501390

@@ -1381,7 +1421,9 @@ def test_one_param_array_matches_scalar(name, draw, p):
13811421
((7,), (7,), 7, (7,)),
13821422
],
13831423
)
1384-
def test_two_param_array_broadcast_shapes(loc_shape, scale_shape, size, expected):
1424+
def test_two_param_array_broadcast_shapes(
1425+
loc_shape, scale_shape, size, expected
1426+
):
13851427
loc = np.zeros(loc_shape) if loc_shape else 0.0
13861428
scale = np.ones(scale_shape) if scale_shape else 1.0
13871429
assert rnd.MKLRandomState(5).normal(loc, scale, size).shape == expected
@@ -1392,6 +1434,30 @@ def test_two_param_array_size_incompatible():
13921434
rnd.MKLRandomState(5).normal(np.zeros(5), np.ones(5), 3)
13931435

13941436

1437+
@pytest.mark.parametrize(
1438+
"name",
1439+
[
1440+
"normal",
1441+
"uniform",
1442+
"exponential",
1443+
"laplace",
1444+
"gumbel",
1445+
"logistic",
1446+
"rayleigh",
1447+
"lognormal",
1448+
],
1449+
)
1450+
@pytest.mark.parametrize("param_shape,size", [((1, 4), (4,)), ((1,), ())])
1451+
def test_array_size_rejects_extra_parameter_dimensions(name, param_shape, size):
1452+
state = rnd.MKLRandomState(5)
1453+
reference = rnd.MKLRandomState(5)
1454+
with pytest.raises(ValueError, match="size is not compatible with inputs"):
1455+
getattr(state, name)(np.full(param_shape, 0.5), size=size)
1456+
np.testing.assert_array_equal(
1457+
state.random_sample(32), reference.random_sample(32)
1458+
)
1459+
1460+
13951461
def test_randomdist_vonmises(randomdist):
13961462
rnd.seed(randomdist.seed, brng=randomdist.brng)
13971463
actual = rnd.vonmises(mu=1.23, kappa=1.54, size=(3, 2))

0 commit comments

Comments
 (0)