Skip to content

fix: validate the sampling method and reject an empty mean in multinormal_cholesky - #189

Open
antonwolfy wants to merge 2 commits into
masterfrom
fix/method-validation-and-empty-mean
Open

antonwolfy wants to merge 2 commits into
masterfrom
fix/method-validation-and-empty-mean

Conversation

@antonwolfy

Copy link
Copy Markdown
Collaborator

Follow-up to the review of #187, which turned up three unrelated behaviours in the Python layer. This PR does not depend on #187 and does not touch the same code.

Problem

  1. Integer-like method ids were misrouted. choose_method returned any value that compared equal to a method id, e.g. np.int64(0), np.int64(2), False or 1.0. The callers then test the result with is, so those values fell through to the final else branch, which is BoxMuller. For example multinormal_cholesky(..., method=np.int64(2)) silently ran BoxMuller instead of BoxMuller2, and method=False ran BoxMuller instead of the default.
  2. Unrecognized methods were silently replaced by the default. A misspelled name, None, or an unsupported method for that function (e.g. "Box-Muller2" for lognormal, which only supports ICDF and BoxMuller) got the default method without any notice.
  3. multinormal_cholesky with an empty mean raised ZeroDivisionError (PyArray_SIZE(...) // dim with dim == 0). NumPy's multivariate_normal raises ValueError for the same input.

Changes

  • choose_method (mkl_random/mklrand.pyx) now accepts strings from the function's alias table and integers (including NumPy integers) matching one of its ids, and returns the module-level constant, so the existing is comparisons keep working.
  • bool, float and any other unrecognized value emit a UserWarning and fall back to the function's default method, in the same style as an unrecognized brng. Results for those inputs are unchanged apart from the new warning, except where the old code misrouted them (item 1).
  • multinormal_cholesky raises ValueError("mean must have at least one element") for an empty mean. oneMKL's vdRngGaussianMV also requires dimen >= 1.
  • test_randomdist_lognormal asked lognormal for "Box-Muller2", which it does not support and silently treated as ICDF. It now asserts the warning explicitly; the expected values are unchanged.
  • CHANGELOG.md: one Changed and two Fixed entries.

Behaviour change to be aware of

Code that passes an unsupported or misspelled method still gets the same numbers as before, but now sees a UserWarning. I chose a warning over an exception to stay backward compatible, consistent with how an unrecognized brng is handled (gh-177). Raising ValueError instead would be a small change in choose_method if that is preferred.

Testing

New tests in mkl_random/tests/test_random.py:

  • test_multinormal_cholesky_empty_mean: both the 2-D and the packed 1-D empty Cholesky factor raise ValueError.
  • test_multinormal_cholesky_integer_method: for each of ICDF, BoxMuller, BoxMuller2, the ids passed as int, np.int64 and np.uint8 give exactly the same draws as the name, and emit no warning.
  • test_unrecognized_method_warns_and_uses_default: "bogus", 7, -1, 1.0, None, False, True and [1] warn and give the default (ICDF) result.
  • test_unrecognized_method_uses_each_functions_default: the fallback is the called function's own default (poisson falls back to POISNORM, not ICDF).

Unlike the tests in #187, these fail on master: running them against a master build, 15 of the 20 cases fail (the other 5 are cases master already handled, such as np.int64(1), which happened to land on BoxMuller). On this branch all 20 pass.

The full suite passes (358 passed, 24 skipped), also with -W error::UserWarning, which confirms that no other call in the test suite triggers the new warning.

…rmal_cholesky

choose_method returned any value that compared equal to a method id, so
np.int64(0), np.int64(2), False and 1.0 passed through unchanged. The
callers then compare the result with `is`, so those values were routed to
the final else branch (BoxMuller) instead of the method they named.
Unrecognized values, such as a misspelled name, were mapped to the
default method without any notice.

- choose_method now accepts strings from the alias table and integers
  (including NumPy integers) matching an id, and returns the module-level
  constant so the `is` comparisons keep working.
- bool and float, which compare equal to the ids but are not valid
  specifications, and any other unrecognized value now emit a
  UserWarning and fall back to the function's default, like an
  unrecognized brng does.
- multinormal_cholesky raises ValueError for an empty mean instead of
  ZeroDivisionError.
- test_randomdist_lognormal asked lognormal for "Box-Muller2", which it
  does not support and silently treated as ICDF; it now asserts the
  warning.
…tests

Place them next to test_normal_family_array_methods so they do not touch
the same lines as other pull requests adding multinormal_cholesky tests.
@antonwolfy antonwolfy self-assigned this Oct 6, 2026
@antonwolfy antonwolfy added this to the 1.6.0 release milestone Oct 6, 2026

This branch has not been deployed

No deployments
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.

1 participant