From e4c3b390932c2a8b77816a774e94634b06c607a8 Mon Sep 17 00:00:00 2001 From: "Christopher J. Wood" Date: Wed, 5 Aug 2020 18:09:47 -0400 Subject: [PATCH] Allow Generator object for seed in QuantumVolume circuit (#4867) * Fix RNG in QuantumVolume circuit * Add seed back to circuit name Note that if seed is a generator object the name contain [num_qubits, depth, Generator(PCG64)at] * Add reno * Add labels back to unitaries This means we are not using the shared Generator object, but instead instantiating new generator objects with fixed integer seeds for each call to random_unitary. * fixup * Update releasenotes/notes/quantum_volume_rng-2e6f46e3821aebeb.yaml Co-authored-by: Matthew Treinish Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- qiskit/circuit/library/quantum_volume.py | 25 ++++++++++++------- .../quantum_volume_rng-2e6f46e3821aebeb.yaml | 5 ++++ test/python/circuit/test_library.py | 17 ++++++++++--- 3 files changed, 34 insertions(+), 13 deletions(-) create mode 100644 releasenotes/notes/quantum_volume_rng-2e6f46e3821aebeb.yaml diff --git a/qiskit/circuit/library/quantum_volume.py b/qiskit/circuit/library/quantum_volume.py index 56193ca3e11e..8dca89ba5069 100644 --- a/qiskit/circuit/library/quantum_volume.py +++ b/qiskit/circuit/library/quantum_volume.py @@ -14,7 +14,7 @@ """Quantum Volume model circuit.""" -from typing import Optional +from typing import Optional, Union import numpy as np from qiskit.quantum_info.random import random_unitary @@ -62,30 +62,37 @@ class QuantumVolume(QuantumCircuit): def __init__(self, num_qubits: int, depth: Optional[int] = None, - seed: Optional[int] = None, + seed: Optional[Union[int, np.random.Generator]] = None, classical_permutation: bool = True) -> None: """Create quantum volume model circuit of size num_qubits x depth. Args: num_qubits: number of active qubits in model circuit. depth: layers of SU(4) operations in model circuit. - seed: randomization seed. + seed: Random number generator or generator seed. classical_permutation: use classical permutations at every layer, rather than quantum. """ - depth = depth or num_qubits # how many layers of SU(4) - width = int(np.floor(num_qubits/2)) # how many SU(4)s fit in each layer - + # Initialize RNG if seed is None: rng_set = np.random.default_rng() seed = rng_set.integers(low=1, high=1000) + if isinstance(seed, np.random.Generator): + rng = seed + else: + rng = np.random.default_rng(seed) + # Parameters + depth = depth or num_qubits # how many layers of SU(4) + width = int(np.floor(num_qubits/2)) # how many SU(4)s fit in each layer name = "quantum_volume_" + str([num_qubits, depth, seed]).replace(' ', '') - super().__init__(num_qubits, name=name) - rng = np.random.default_rng(seed) - + # Generator random unitary seeds in advance. + # Note that this means we are constructing multiple new generator + # objects from low-entropy integer seeds rather than pass the shared + # generator object to the random_unitary function. This is done so + # that we can use the integer seed as a label for the generated gates. unitary_seeds = rng.integers(low=1, high=1000, size=[depth, width]) # For each layer, generate a permutation of qubits diff --git a/releasenotes/notes/quantum_volume_rng-2e6f46e3821aebeb.yaml b/releasenotes/notes/quantum_volume_rng-2e6f46e3821aebeb.yaml new file mode 100644 index 000000000000..a4541e1fa437 --- /dev/null +++ b/releasenotes/notes/quantum_volume_rng-2e6f46e3821aebeb.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Allow passing in a Numpy random ``Generator`` object for the ``seed`` kwarg + in :class:`qiskit.circuit.library.QuantumVolume`. diff --git a/test/python/circuit/test_library.py b/test/python/circuit/test_library.py index 8fb150d224b5..e7302ed7d89e 100644 --- a/test/python/circuit/test_library.py +++ b/test/python/circuit/test_library.py @@ -274,11 +274,20 @@ class TestQuantumVolumeLibrary(QiskitTestCase): def test_qv(self): """Test qv circuit.""" - circuit = QuantumVolume(2, 2, seed=2, classical_permutation=False) + seed = 10203 + rng1 = np.random.default_rng(seed) + rng2 = np.random.default_rng(seed) + + depth = 2 + width = 1 + circuit = QuantumVolume(2, depth, seed=rng1, classical_permutation=False) + expected = QuantumCircuit(2) - expected.swap(0, 1) - expected.append(random_unitary(4, seed=837), [0, 1]) - expected.append(random_unitary(4, seed=262), [0, 1]) + unitary_seeds = rng2.integers(low=1, high=1000, size=[depth, width]) + for d in range(depth): + if rng2.permutation([0, 1]).tolist() == [1, 0]: + expected.swap(0, 1) + expected.append(random_unitary(4, seed=unitary_seeds[d][0]), [0, 1]) expected = Operator(expected) simulated = Operator(circuit) self.assertTrue(expected.equiv(simulated))