Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initialize DefaultQubit2 local rng from global rng if no seed provided #4394

Merged
merged 9 commits into from
Jul 28, 2023
4 changes: 4 additions & 0 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

<h3>Breaking changes 💔</h3>

* `Operator.expand` now uses the output of `Operator.decomposition` instead of what it queues.
Expand Down
17 changes: 10 additions & 7 deletions pennylane/devices/experimental/default_qubit_2.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,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``.
Expand Down Expand Up @@ -134,9 +136,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

Expand Down
34 changes: 32 additions & 2 deletions tests/devices/experimental/test_default_qubit_2.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -1257,7 +1257,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)
Expand All @@ -1278,6 +1278,36 @@ 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_num = dev._rng.random() # pylint: disable=protected-access

np.random.seed(42)
dev2 = DefaultQubit2(seed=None)
second_num = dev2._rng.random() # pylint: disable=protected-access

assert not qml.math.allclose(first_num, second_num)
albi3ro marked this conversation as resolved.
Show resolved Hide resolved

albi3ro marked this conversation as resolved.
Show resolved Hide resolved

class TestHamiltonianSamples:
"""Test that the measure_with_samples function works as expected for
Expand Down
Loading