diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 07afc84ebfb..d2f91dff007 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -88,6 +88,10 @@ * When given a callable, `qml.ctrl` now does its custom pre-processing on all queued operators from the callable. [(#4370)](https://github.com/PennyLaneAI/pennylane/pull/4370) +* If no seed is specified on initialization with `DefaultQubit2`, the local random number generator will be + seeded from on the NumPy's global random number generator. + [(#4394)](https://github.com/PennyLaneAI/pennylane/pull/4394) + * The experimental `DefaultQubit2` device now supports computing VJPs and JVPs using the adjoint method. [(#4374)](https://github.com/PennyLaneAI/pennylane/pull/4374) diff --git a/pennylane/devices/experimental/default_qubit_2.py b/pennylane/devices/experimental/default_qubit_2.py index 8bd3fb7dd1a..db074d82ce0 100644 --- a/pennylane/devices/experimental/default_qubit_2.py +++ b/pennylane/devices/experimental/default_qubit_2.py @@ -44,12 +44,14 @@ class DefaultQubit2(Device): """A PennyLane device written in Python and capable of backpropagation derivatives. - Args: - seed (Union[None, int, array_like[int], SeedSequence, BitGenerator, Generator]): A - seed-like parameter matching that of ``seed`` for ``numpy.random.default_rng``. - If no value is provided, a default RNG will be used. - - max_workers (int): A ``ProcessPoolExecutor`` executes tapes asynchronously + Keyword Args: + seed="global" (Union[str, None, int, array_like[int], SeedSequence, BitGenerator, Generator]): A + seed-like parameter matching that of ``seed`` for ``numpy.random.default_rng`` or + a request to seed from numpy's global random number generator. + The default, ``seed="global"`` pulls a seed from NumPy's global generator. ``seed=None`` + will pull a seed from the OS entropy. + + max_workers=None (int): A ``ProcessPoolExecutor`` executes tapes asynchronously using a pool of at most ``max_workers`` processes. If ``max_workers`` is ``None``, only the current process executes tapes. If you experience any issue, say using JAX, TensorFlow, Torch, try setting ``max_workers`` to ``None``. @@ -135,9 +137,10 @@ def name(self): """The name of the device.""" return "default.qubit.2" - def __init__(self, seed=None, max_workers=None) -> None: + def __init__(self, seed="global", max_workers=None) -> None: super().__init__() self._max_workers = max_workers + seed = np.random.randint(0, high=10000000) if seed == "global" else seed self._rng = np.random.default_rng(seed) self._debugger = None diff --git a/tests/devices/experimental/test_default_qubit_2.py b/tests/devices/experimental/test_default_qubit_2.py index 8de95f5d994..f7106062f86 100644 --- a/tests/devices/experimental/test_default_qubit_2.py +++ b/tests/devices/experimental/test_default_qubit_2.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """Tests for default qubit 2.""" -# pylint: disable=import-outside-toplevel +# pylint: disable=import-outside-toplevel, no-member import pytest @@ -1456,7 +1456,7 @@ def test_different_executions(self, measurements, max_workers): [qml.sample(wires=0), qml.expval(qml.PauliZ(0)), qml.probs(wires=0)], ], ) - def test_global_seed(self, measurements, max_workers): + def test_global_seed_and_device_seed(self, measurements, max_workers): """Test that a global seed does not affect the result of devices provided with a seed""" qs = qml.tape.QuantumScript([qml.Hadamard(0)], measurements, shots=1000) @@ -1477,6 +1477,47 @@ def test_global_seed(self, measurements, max_workers): assert all(np.all(res1 == res2) for res1, res2 in zip(result1, result2)) + def test_global_seed_no_device_seed_by_default(self): + """Test that the global numpy seed initializes the rng if device seed is none.""" + np.random.seed(42) + dev = DefaultQubit2() + first_num = dev._rng.random() # pylint: disable=protected-access + + np.random.seed(42) + dev2 = DefaultQubit2() + second_num = dev2._rng.random() # pylint: disable=protected-access + + assert qml.math.allclose(first_num, second_num) + + np.random.seed(42) + dev2 = DefaultQubit2(seed="global") + third_num = dev2._rng.random() # pylint: disable=protected-access + + assert qml.math.allclose(third_num, first_num) + + def test_None_seed_not_using_global_rng(self): + """Test that if the seed is None, it is uncorrelated with the global rng.""" + np.random.seed(42) + dev = DefaultQubit2(seed=None) + first_nums = dev._rng.random(10) # pylint: disable=protected-access + + np.random.seed(42) + dev2 = DefaultQubit2(seed=None) + second_nums = dev2._rng.random(10) # pylint: disable=protected-access + + assert not qml.math.allclose(first_nums, second_nums) + + def test_rng_as_seed(self): + """Test that a PRNG can be passed as a seed.""" + rng1 = np.random.default_rng(42) + first_num = rng1.random() + + rng = np.random.default_rng(42) + dev = DefaultQubit2(seed=rng) + second_num = dev._rng.random() # pylint: disable=protected-access + + assert qml.math.allclose(first_num, second_num) + class TestHamiltonianSamples: """Test that the measure_with_samples function works as expected for