From 3b0c1b613727f4cb22269c72c74900c160f21f09 Mon Sep 17 00:00:00 2001 From: Lee James O'Riordan Date: Wed, 3 Apr 2024 16:05:35 -0400 Subject: [PATCH] Ensure bounded values for numpy binomial (#5447) ### Before submitting Please complete the following checklist when submitting a PR: - [x] All new features must include a unit test. If you've fixed a bug or added code that should be tested, add a test to the test directory! - [x] All new functions and code must be clearly commented and documented. If you do make documentation changes, make sure that the docs build and render correctly by running `make docs`. - [x] Ensure that the test suite passes, by running `make test`. - [x] Add a new entry to the `doc/releases/changelog-dev.md` file, summarizing the change, and including a link back to the PR. - [x] The PennyLane source code conforms to [PEP8 standards](https://www.python.org/dev/peps/pep-0008/). We check all of our code against [Pylint](https://www.pylint.org/). To lint modified files, simply `pip install pylint`, and then run `pylint pennylane/path/to/file.py`. When all the above are checked, delete everything above the dashed line and fill in the pull request template. ------------------------------------------------------------------------------------------------------------ **Context:** A [user](https://discuss.pennylane.ai/t/multiple-resets-crash-spsaoptimizer-in-ver-0-35/4322/7) reports errors when running MCM circuits, which fail due to numpy-defined bounds checks in `qml.random.binomial` which should be `0<= x <=1`. This error is due to the input data to `binomial` being supplied with 1.0000000000000002 from another numpy layer, which triggers this bounds check. This PR rounds towards 1.0 if an associated ValueError is thrown. **Description of the Change:** Adds a try-except block to catch and round if the boundary check fails. **Benefits:** Ensures continued use of MCM circuits. **Possible Drawbacks:** Depending on the occurrence frequency of boundary failures, replacing the try-except block may be better handled by pre-validation against differences between unity and unity+macheps. **Related GitHub Issues:** --- doc/releases/changelog-dev.md | 3 +++ pennylane/devices/qubit/apply_operation.py | 10 +++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 00011f80627..a48d37fd7ab 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -294,6 +294,9 @@

Bug fixes 🐛

+* Avoid bounded value failures due to numerical noise with calls to `np.random.binomial`. + [(#5447)](https://github.com/PennyLaneAI/pennylane/pull/5447) + * Using `@` with legacy Hamiltonian instances now properly de-queues the previously existing operations. [(#5454)](https://github.com/PennyLaneAI/pennylane/pull/5455) diff --git a/pennylane/devices/qubit/apply_operation.py b/pennylane/devices/qubit/apply_operation.py index ca9db1063ad..61ea0956a47 100644 --- a/pennylane/devices/qubit/apply_operation.py +++ b/pennylane/devices/qubit/apply_operation.py @@ -265,7 +265,15 @@ def apply_mid_measure( return np.zeros_like(state) wire = op.wires probs = qml.devices.qubit.measure(qml.probs(wire), state) - sample = np.random.binomial(1, probs[1]) + + try: # pragma: no cover + sample = np.random.binomial(1, probs[1]) + except ValueError as e: # pragma: no cover + if probs[1] > 1: # MachEps error, safe to catch + sample = np.random.binomial(1, np.round(probs[1], 15)) + else: # Other general error, continue to fail + raise e + mid_measurements[op] = sample if op.postselect is not None and sample != op.postselect: return np.zeros_like(state)