Skip to content

Commit 50f2965

Browse files
authored
Use Rust generator function for QuantumVolume class (#13283)
* Use Rust generator function for QuantumVolume class In #13238 we added a new function quantum_volume for generating a quantum volume model circuit with the eventual goal of replacing the existing QuantumVolume class. This new function is ~10x faster to generate the circuit than the existing python class. This commit builds off of that to internally call the new function for generating the circuit from the class. While the plan is to deprecate and remove the class for Qiskit 2.0 until that time we can give a performance boost to users of it for the lifespan of the 1.x release series. * Use seed_name in string generation * Fix rng call
1 parent 344cb7a commit 50f2965

2 files changed

Lines changed: 47 additions & 20 deletions

File tree

qiskit/circuit/library/quantum_volume.py

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -83,39 +83,49 @@ def __init__(
8383
depth = depth or num_qubits # how many layers of SU(4)
8484
width = num_qubits // 2 # how many SU(4)s fit in each layer
8585
rng = seed if isinstance(seed, np.random.Generator) else np.random.default_rng(seed)
86-
if seed is None:
86+
seed_name = seed
87+
if seed_name is None:
8788
# Get the internal entropy used to seed the default RNG, if no seed was given. This
8889
# stays in the output name, so effectively stores a way of regenerating the circuit.
8990
# This is just best-effort only, for backwards compatibility, and isn't critical (if
9091
# someone needs full reproducibility, they should be manually controlling the seeding).
91-
seed = getattr(getattr(rng.bit_generator, "seed_seq", None), "entropy", None)
92+
seed_name = getattr(getattr(rng.bit_generator, "seed_seq", None), "entropy", None)
9293

9394
super().__init__(
94-
num_qubits, name="quantum_volume_" + str([num_qubits, depth, seed]).replace(" ", "")
95+
num_qubits,
96+
name="quantum_volume_" + str([num_qubits, depth, seed_name]).replace(" ", ""),
9597
)
96-
base = self if flatten else QuantumCircuit(num_qubits, name=self.name)
97-
98-
# For each layer, generate a permutation of qubits
99-
# Then generate and apply a Haar-random SU(4) to each pair
100-
unitaries = scipy.stats.unitary_group.rvs(4, depth * width, rng).reshape(depth, width, 4, 4)
101-
qubits = tuple(base.qubits)
102-
for row in unitaries:
103-
perm = rng.permutation(num_qubits)
104-
if classical_permutation:
105-
for w, unitary in enumerate(row):
106-
gate = UnitaryGate(unitary, check_input=False, num_qubits=2)
107-
qubit = 2 * w
108-
base._append(
109-
CircuitInstruction(gate, (qubits[perm[qubit]], qubits[perm[qubit + 1]]))
110-
)
98+
if classical_permutation:
99+
if seed is not None:
100+
max_value = np.iinfo(np.int64).max
101+
seed = rng.integers(max_value, dtype=np.int64)
102+
qv_circ = quantum_volume(num_qubits, depth, seed)
103+
qv_circ.name = self.name
104+
if flatten:
105+
self.compose(qv_circ, inplace=True)
111106
else:
107+
self._append(CircuitInstruction(qv_circ.to_instruction(), tuple(self.qubits)))
108+
else:
109+
if seed is None:
110+
seed = seed_name
111+
112+
base = self if flatten else QuantumCircuit(num_qubits, name=self.name)
113+
114+
# For each layer, generate a permutation of qubits
115+
# Then generate and apply a Haar-random SU(4) to each pair
116+
unitaries = scipy.stats.unitary_group.rvs(4, depth * width, rng).reshape(
117+
depth, width, 4, 4
118+
)
119+
qubits = tuple(base.qubits)
120+
for row in unitaries:
121+
perm = rng.permutation(num_qubits)
112122
base._append(CircuitInstruction(PermutationGate(perm), qubits))
113123
for w, unitary in enumerate(row):
114124
gate = UnitaryGate(unitary, check_input=False, num_qubits=2)
115125
qubit = 2 * w
116126
base._append(CircuitInstruction(gate, qubits[qubit : qubit + 2]))
117-
if not flatten:
118-
self._append(CircuitInstruction(base.to_instruction(), tuple(self.qubits)))
127+
if not flatten:
128+
self._append(CircuitInstruction(base.to_instruction(), tuple(self.qubits)))
119129

120130

121131
def quantum_volume(

releasenotes/notes/add-qv-function-a8990e248d5e7e1a.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,20 @@ features_circuits:
99
a :class:`.QuantumCircuit` object instead of building a subclass object. The second is
1010
that this new function is multithreaded and implemented in rust so it generates the output
1111
circuit ~10x faster than the :class:`.QuantumVolume` class.
12+
- |
13+
Improved the runtime performance of constructing the
14+
:class:`.QuantumVolume` class with the ``classical_permutation`` argument set
15+
to ``True``. Internally it now calls the :func:`.quantum_volume`
16+
function which is written in Rust which is ~10x faster to generate a
17+
quantum volume circuit.
18+
19+
upgrade_circuits:
20+
- |
21+
The :class:`.QuantumVolume` class will generate circuits with
22+
different unitary matrices and permutations for a given seed value from
23+
the previous Qiskit release. This is due to using a new internal random
24+
number generator for the circuit generation that will generate the circuit
25+
more quickly. If you need an exact circuit with the same seed you can
26+
use the previous release of Qiskit and generate the circuit with the
27+
``flatten=True`` argument and export the circuit with :func:`.qpy.dump`
28+
and then load it with this release.

0 commit comments

Comments
 (0)