mirror of https://github.com/Qiskit/qiskit.git
Fix support for standalone registers in qpy format (#6523)
* Fix support for standalone registers in qpy format This commit tweaks the qpy register format slightly to enable differentiating between standalone registers (ones that own their bits) and registers that contain pre-existing bits. Previously this didn't work in all cases with standalone registers (it only worked assuming the qubits in a register were a contiguous set of qubit indices in the circuit). Normally this type of change would require a format version bump for backwards compatibility (see #6419 for a PoC showing this) however because we have yet to include qpy in a release we have an opportunity to fix this prior to release in the same initial version. * Fix sphinx syntax * Fix docstring whitespace * Fix rebase error * Update qiskit/circuit/qpy_serialization.py Co-authored-by: Kevin Krsulich <kevin@krsulich.net> * Expand docstring * Fix bugs and expand tests * Add mixed standalone shared register circuit test In the process of adding this test a bug was found in the DAGCircuit __eq__ method (which is used for QuantumCircuit too). The equality of circuits was failing based on the insertion order of registers which doesn't matter. As long as the bit indices are in order in the register (ie qr[2] -> 3 is consistent between both circuits) the order qr was inserted into the circuit doesn't matter. This was causing a failure in the new test because qpy deserialization always loads standalone registers first even though the bit order is properly preserved. To fix this the dagcircuit __eq__ method is update to only care about the bit indices (both register and circuit) and ignore register insertion order. * Add comments on the read register flow * Preserve register insertion order on circuit deserialization This commit preserves the register insertion order when deserializing a circuit form qpy. Previously this wasn't being preserved and standalone registers were always being added first. This also reverts the change made to dagcircuit.__eq__ made in an earlier commit to workaround the earlier limitation. * Handle out of order register bit insertion * Fix endianness bug in register index array * Preserve the label for instructions This commit modifies the instruction payload format to also include the label of an instruction in the circuit. Previously we were just ignoring the label which meant that it was omitted from the qpy data and any labels would not be present in the deserialized circuit. * Fix duplicate test_name * Fix lint * Assert label equality in label tests * Fix label init condition * Remove unecessary condition * Deduplicate register creation Co-authored-by: Kevin Krsulich <kevin@krsulich.net>
This commit is contained in:
parent
fcedd7e17a
commit
456d036ade
|
@ -84,7 +84,7 @@ class Instruction:
|
|||
# NOTE: The conditional statement checking if the `_label` attribute is
|
||||
# already set is a temporary work around that can be removed after
|
||||
# the next stable qiskit-aer release
|
||||
if label is not None or not hasattr(self, "_label"):
|
||||
if not hasattr(self, "_label"):
|
||||
self._label = label
|
||||
# tuple (ClassicalRegister, int), tuple (Clbit, bool) or tuple (Clbit, int)
|
||||
# when the instruction has a conditional ("if")
|
||||
|
|
|
@ -19,12 +19,6 @@ QPY serialization (:mod:`qiskit.circuit.qpy_serialization`)
|
|||
|
||||
.. currentmodule:: qiskit.circuit.qpy_serialization
|
||||
|
||||
.. warning::
|
||||
|
||||
QPY serialization is still an experimental feature, the API and/or
|
||||
forward compatibility are not yet guaranteed. Future versions of Qiskit
|
||||
may not be fully compatible.
|
||||
|
||||
.. autosummary::
|
||||
|
||||
load
|
||||
|
@ -103,6 +97,7 @@ as:
|
|||
|
||||
struct {
|
||||
char type;
|
||||
_Bool standalone;
|
||||
uint32_t size;
|
||||
unit16_t name_size;
|
||||
}
|
||||
|
@ -115,6 +110,23 @@ uint32_t values of size ``size`` that contains a map of the register's index to
|
|||
the circuit's qubit index. For example, array element 0's value is the index
|
||||
of the ``register[0]``'s position in the containing circuit's qubits list.
|
||||
|
||||
The standalone boolean determines whether the register is constructed as a
|
||||
standalone register that was added to the circuit or was created from existing
|
||||
bits. A register is considered standalone if it has bits constructed solely
|
||||
as part of it, for example::
|
||||
|
||||
qr = QuantumRegister(2)
|
||||
qc = QuantumCircuit(qr)
|
||||
|
||||
the register ``qr`` would be a standalone register. While something like::
|
||||
|
||||
bits = [Qubit(), Qubit()]
|
||||
qr = QuantumRegister(bits=bits)
|
||||
qc = QuantumCircuit(bits=bits)
|
||||
|
||||
``qr`` would have ``standalone`` set to ``False``.
|
||||
|
||||
|
||||
CUSTOM_DEFINITIONS
|
||||
------------------
|
||||
|
||||
|
@ -158,6 +170,7 @@ The contents of INSTRUCTIONS is a list of INSTRUCTION metadata objects
|
|||
|
||||
struct {
|
||||
uint16_t name_size;
|
||||
uint16_t label_size;
|
||||
uint16_t num_parameters;
|
||||
uint32_t num_qargs;
|
||||
uint32_t num_cargs;
|
||||
|
@ -169,9 +182,11 @@ The contents of INSTRUCTIONS is a list of INSTRUCTION metadata objects
|
|||
This metadata object is immediately followed by ``name_size`` bytes of utf8 bytes
|
||||
for the ``name``. ``name`` here is the Qiskit class name for the Instruction
|
||||
class if it's defined in Qiskit. Otherwise it falls back to the custom
|
||||
instruction name. Following the ``name`` bytes if ``has_conditional`` is ``True``
|
||||
then there are ``conditonal_reg_name_size`` bytes of utf8 data for the name of
|
||||
the condtional register name.
|
||||
instruction name. Following the ``name`` bytes there are ``label_size`` bytes of
|
||||
utf8 data for the label if one was set on the instruction. Following the label
|
||||
bytes if ``has_conditional`` is ``True`` then there are
|
||||
``conditonal_reg_name_size`` bytes of utf8 data for the name of the condtional
|
||||
register name.
|
||||
|
||||
This is immediately followed by the INSTRUCTION_ARG structs for the list of
|
||||
arguments of that instruction. These are in the order of all quantum arguments
|
||||
|
@ -353,8 +368,8 @@ CUSTOM_DEFINITION_SIZE = struct.calcsize(CUSTOM_DEFINITION_PACK)
|
|||
|
||||
|
||||
# REGISTER binary format
|
||||
REGISTER = namedtuple("REGISTER", ["type", "size", "name_size"])
|
||||
REGISTER_PACK = "!1cIH"
|
||||
REGISTER = namedtuple("REGISTER", ["type", "standalone", "size", "name_size"])
|
||||
REGISTER_PACK = "!1c?IH"
|
||||
REGISTER_SIZE = struct.calcsize(REGISTER_PACK)
|
||||
|
||||
# INSTRUCTION binary format
|
||||
|
@ -362,6 +377,7 @@ INSTRUCTION = namedtuple(
|
|||
"INSTRUCTION",
|
||||
[
|
||||
"name_size",
|
||||
"label_size",
|
||||
"num_parameters",
|
||||
"num_qargs",
|
||||
"num_cargs",
|
||||
|
@ -370,7 +386,7 @@ INSTRUCTION = namedtuple(
|
|||
"value",
|
||||
],
|
||||
)
|
||||
INSTRUCTION_PACK = "!HHII?Hq"
|
||||
INSTRUCTION_PACK = "!HHHII?Hq"
|
||||
INSTRUCTION_SIZE = struct.calcsize(INSTRUCTION_PACK)
|
||||
# Instruction argument format
|
||||
INSTRUCTION_ARG = namedtuple("INSTRUCTION_ARG", ["type", "size"])
|
||||
|
@ -412,22 +428,15 @@ def _read_registers(file_obj, num_registers):
|
|||
for _reg in range(num_registers):
|
||||
register_raw = file_obj.read(REGISTER_SIZE)
|
||||
register = struct.unpack(REGISTER_PACK, register_raw)
|
||||
name = file_obj.read(register[2]).decode("utf8")
|
||||
REGISTER_ARRAY_PACK = "%sI" % register[1]
|
||||
name = file_obj.read(register[3]).decode("utf8")
|
||||
standalone = register[1]
|
||||
REGISTER_ARRAY_PACK = "!%sI" % register[2]
|
||||
bit_indices_raw = file_obj.read(struct.calcsize(REGISTER_ARRAY_PACK))
|
||||
bit_indices = struct.unpack(REGISTER_ARRAY_PACK, bit_indices_raw)
|
||||
bit_indices = list(struct.unpack(REGISTER_ARRAY_PACK, bit_indices_raw))
|
||||
if register[0].decode("utf8") == "q":
|
||||
registers["q"][name] = {}
|
||||
registers["q"][name]["register"] = QuantumRegister(register[1], name)
|
||||
registers["q"][name]["index_map"] = dict(
|
||||
zip(bit_indices, registers["q"][name]["register"])
|
||||
)
|
||||
registers["q"][name] = (standalone, bit_indices)
|
||||
else:
|
||||
registers["c"][name] = {}
|
||||
registers["c"][name]["register"] = ClassicalRegister(register[1], name)
|
||||
registers["c"][name]["index_map"] = dict(
|
||||
zip(bit_indices, registers["c"][name]["register"])
|
||||
)
|
||||
registers["c"][name] = (standalone, bit_indices)
|
||||
return registers
|
||||
|
||||
|
||||
|
@ -477,20 +486,22 @@ def _read_instruction(file_obj, circuit, registers, custom_instructions):
|
|||
instruction_raw = file_obj.read(INSTRUCTION_SIZE)
|
||||
instruction = struct.unpack(INSTRUCTION_PACK, instruction_raw)
|
||||
name_size = instruction[0]
|
||||
label_size = instruction[1]
|
||||
qargs = []
|
||||
cargs = []
|
||||
params = []
|
||||
gate_name = file_obj.read(name_size).decode("utf8")
|
||||
num_qargs = instruction[2]
|
||||
num_cargs = instruction[3]
|
||||
num_params = instruction[1]
|
||||
has_condition = instruction[4]
|
||||
register_name_size = instruction[5]
|
||||
label = file_obj.read(label_size).decode("utf8")
|
||||
num_qargs = instruction[3]
|
||||
num_cargs = instruction[4]
|
||||
num_params = instruction[2]
|
||||
has_condition = instruction[5]
|
||||
register_name_size = instruction[6]
|
||||
condition_register = file_obj.read(register_name_size).decode("utf8")
|
||||
condition_value = instruction[6]
|
||||
condition_value = instruction[7]
|
||||
condition_tuple = None
|
||||
if has_condition:
|
||||
condition_tuple = (registers["c"][condition_register]["register"], condition_value)
|
||||
condition_tuple = (registers["c"][condition_register], condition_value)
|
||||
qubit_indices = dict(enumerate(circuit.qubits))
|
||||
clbit_indices = dict(enumerate(circuit.clbits))
|
||||
# Load Arguments
|
||||
|
@ -536,6 +547,8 @@ def _read_instruction(file_obj, circuit, registers, custom_instructions):
|
|||
if gate_name in ("Gate", "Instruction"):
|
||||
inst_obj = _parse_custom_instruction(custom_instructions, gate_name, params)
|
||||
inst_obj.condition = condition_tuple
|
||||
if label_size > 0:
|
||||
inst_obj.label = label
|
||||
circuit._append(inst_obj, qargs, cargs)
|
||||
return
|
||||
elif hasattr(library, gate_name):
|
||||
|
@ -549,6 +562,8 @@ def _read_instruction(file_obj, circuit, registers, custom_instructions):
|
|||
elif gate_name in custom_instructions:
|
||||
inst_obj = _parse_custom_instruction(custom_instructions, gate_name, params)
|
||||
inst_obj.condition = condition_tuple
|
||||
if label_size > 0:
|
||||
inst_obj.label = label
|
||||
circuit._append(inst_obj, qargs, cargs)
|
||||
return
|
||||
else:
|
||||
|
@ -557,6 +572,8 @@ def _read_instruction(file_obj, circuit, registers, custom_instructions):
|
|||
params = [len(qargs)]
|
||||
gate = gate_class(*params)
|
||||
gate.condition = condition_tuple
|
||||
if label_size > 0:
|
||||
gate.label = label
|
||||
circuit._append(gate, qargs, cargs)
|
||||
|
||||
|
||||
|
@ -674,9 +691,15 @@ def _write_instruction(file_obj, instruction_tuple, custom_instructions, index_m
|
|||
condition_value = instruction_tuple[0].condition[1]
|
||||
|
||||
gate_class_name = gate_class_name.encode("utf8")
|
||||
label = getattr(instruction_tuple[0], "label")
|
||||
if label:
|
||||
label_raw = label.encode("utf8")
|
||||
else:
|
||||
label_raw = b""
|
||||
instruction_raw = struct.pack(
|
||||
INSTRUCTION_PACK,
|
||||
len(gate_class_name),
|
||||
len(label_raw),
|
||||
len(instruction_tuple[0].params),
|
||||
instruction_tuple[0].num_qubits,
|
||||
instruction_tuple[0].num_clbits,
|
||||
|
@ -686,6 +709,7 @@ def _write_instruction(file_obj, instruction_tuple, custom_instructions, index_m
|
|||
)
|
||||
file_obj.write(instruction_raw)
|
||||
file_obj.write(gate_class_name)
|
||||
file_obj.write(label_raw)
|
||||
file_obj.write(condition_register)
|
||||
# Encode instruciton args
|
||||
for qbit in instruction_tuple[1]:
|
||||
|
@ -854,16 +878,18 @@ def _write_circuit(file_obj, circuit):
|
|||
clbit_indices = {bit: index for index, bit in enumerate(circuit.clbits)}
|
||||
if num_registers > 0:
|
||||
for reg in circuit.qregs:
|
||||
standalone = all(bit._register is reg for bit in reg)
|
||||
reg_name = reg.name.encode("utf8")
|
||||
file_obj.write(struct.pack(REGISTER_PACK, b"q", reg.size, len(reg_name)))
|
||||
file_obj.write(struct.pack(REGISTER_PACK, b"q", standalone, reg.size, len(reg_name)))
|
||||
file_obj.write(reg_name)
|
||||
REGISTER_ARRAY_PACK = "%sI" % reg.size
|
||||
REGISTER_ARRAY_PACK = "!%sI" % reg.size
|
||||
file_obj.write(struct.pack(REGISTER_ARRAY_PACK, *(qubit_indices[bit] for bit in reg)))
|
||||
for reg in circuit.cregs:
|
||||
standalone = all(bit._register is reg for bit in reg)
|
||||
reg_name = reg.name.encode("utf8")
|
||||
file_obj.write(struct.pack(REGISTER_PACK, b"c", reg.size, len(reg_name)))
|
||||
file_obj.write(struct.pack(REGISTER_PACK, b"c", standalone, reg.size, len(reg_name)))
|
||||
file_obj.write(reg_name)
|
||||
REGISTER_ARRAY_PACK = "%sI" % reg.size
|
||||
REGISTER_ARRAY_PACK = "!%sI" % reg.size
|
||||
file_obj.write(struct.pack(REGISTER_ARRAY_PACK, *(clbit_indices[bit] for bit in reg)))
|
||||
instruction_buffer = io.BytesIO()
|
||||
custom_instructions = {}
|
||||
|
@ -961,25 +987,91 @@ def _read_circuit(file_obj):
|
|||
num_registers,
|
||||
num_instructions,
|
||||
) = header
|
||||
registers = {}
|
||||
out_registers = {"q": {}, "c": {}}
|
||||
if num_registers > 0:
|
||||
circ = QuantumCircuit(name=name, global_phase=global_phase, metadata=metadata)
|
||||
# TODO Update to handle registers composed of not continuous bit
|
||||
# indices. Right now this only works for standalone registers or
|
||||
# registers composed bit indices that are continuous
|
||||
registers = _read_registers(file_obj, num_registers)
|
||||
for qreg in registers["q"].values():
|
||||
min_index = min(qreg["index_map"].keys())
|
||||
qubits = [Qubit() for i in range(min_index - len(circ.qubits))]
|
||||
if qubits:
|
||||
circ.add_bits(qubits)
|
||||
circ.add_register(qreg["register"])
|
||||
for creg in registers["c"].values():
|
||||
min_index = min(creg["index_map"].keys())
|
||||
clbits = [Clbit() for i in range(min_index - len(circ.clbits))]
|
||||
if clbits:
|
||||
circ.add_bits(clbits)
|
||||
circ.add_register(creg["register"])
|
||||
|
||||
for bit_type_label, bit_type, reg_type in [
|
||||
("q", Qubit, QuantumRegister),
|
||||
("c", Clbit, ClassicalRegister),
|
||||
]:
|
||||
register_bits = set()
|
||||
# Add quantum registers and bits
|
||||
for register_name in registers[bit_type_label]:
|
||||
standalone, indices = registers[bit_type_label][register_name]
|
||||
if standalone:
|
||||
start = min(indices)
|
||||
count = start
|
||||
out_of_order = False
|
||||
for index in indices:
|
||||
if not out_of_order and index != count:
|
||||
out_of_order = True
|
||||
count += 1
|
||||
if index in register_bits:
|
||||
raise QiskitError("Duplicate register bits found")
|
||||
register_bits.add(index)
|
||||
|
||||
num_reg_bits = len(indices)
|
||||
# Create a standlone register of the appropriate length (from
|
||||
# the number of indices in the qpy data) and add it to the circuit
|
||||
reg = reg_type(num_reg_bits, register_name)
|
||||
# If any bits from qreg are out of order in the circuit handle
|
||||
# is case
|
||||
if out_of_order:
|
||||
sorted_indices = np.argsort(indices)
|
||||
for index in sorted_indices:
|
||||
pos = indices[index]
|
||||
if bit_type_label == "q":
|
||||
bit_len = len(circ.qubits)
|
||||
else:
|
||||
bit_len = len(circ.clbits)
|
||||
# Fill any holes between the current register bit and the
|
||||
# next one
|
||||
if pos > bit_len:
|
||||
bits = [bit_type() for _ in range(pos - bit_len)]
|
||||
circ.add_bits(bits)
|
||||
circ.add_bits([reg[index]])
|
||||
circ.add_register(reg)
|
||||
else:
|
||||
if bit_type_label == "q":
|
||||
bit_len = len(circ.qubits)
|
||||
else:
|
||||
bit_len = len(circ.clbits)
|
||||
# If there is a hole between the start of the register and the
|
||||
# current bits and standalone bits to fill the gap.
|
||||
if start > len(circ.qubits):
|
||||
bits = [bit_type() for _ in range(start - bit_len)]
|
||||
circ.add_bits(bit_len)
|
||||
circ.add_register(reg)
|
||||
out_registers[bit_type_label][register_name] = reg
|
||||
else:
|
||||
for index in indices:
|
||||
if bit_type_label == "q":
|
||||
bit_len = len(circ.qubits)
|
||||
else:
|
||||
bit_len = len(circ.clbits)
|
||||
# Add any missing bits
|
||||
bits = [bit_type() for _ in range(index + 1 - bit_len)]
|
||||
circ.add_bits(bits)
|
||||
if index in register_bits:
|
||||
raise QiskitError("Duplicate register bits found")
|
||||
register_bits.add(index)
|
||||
if bit_type_label == "q":
|
||||
bits = [circ.qubits[i] for i in indices]
|
||||
else:
|
||||
bits = [circ.clbits[i] for i in indices]
|
||||
reg = reg_type(name=register_name, bits=bits)
|
||||
circ.add_register(reg)
|
||||
out_registers[bit_type_label][register_name] = reg
|
||||
# If we don't have sufficient bits in the circuit after adding
|
||||
# all the registers add more bits to fill the circuit
|
||||
if len(circ.qubits) < num_qubits:
|
||||
qubits = [Qubit() for _ in range(num_qubits - len(circ.qubits))]
|
||||
circ.add_bits(qubits)
|
||||
if len(circ.clbits) < num_clbits:
|
||||
clbits = [Clbit() for _ in range(num_qubits - len(circ.clbits))]
|
||||
circ.add_bits(clbits)
|
||||
else:
|
||||
circ = QuantumCircuit(
|
||||
num_qubits,
|
||||
|
@ -990,6 +1082,6 @@ def _read_circuit(file_obj):
|
|||
)
|
||||
custom_instructions = _read_custom_instructions(file_obj)
|
||||
for _instruction in range(num_instructions):
|
||||
_read_instruction(file_obj, circ, registers, custom_instructions)
|
||||
_read_instruction(file_obj, circ, out_registers, custom_instructions)
|
||||
|
||||
return circ
|
||||
|
|
|
@ -14,17 +14,23 @@
|
|||
"""Test cases for the circuit qasm_file and qasm_string method."""
|
||||
|
||||
import io
|
||||
import random
|
||||
|
||||
import numpy as np
|
||||
|
||||
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
|
||||
from qiskit.circuit.classicalregister import Clbit
|
||||
from qiskit.circuit.quantumregister import Qubit
|
||||
from qiskit.circuit.random import random_circuit
|
||||
from qiskit.circuit.gate import Gate
|
||||
from qiskit.circuit.library import XGate
|
||||
from qiskit.circuit.instruction import Instruction
|
||||
from qiskit.circuit.parameter import Parameter
|
||||
from qiskit.extensions import UnitaryGate
|
||||
from qiskit.opflow import X, Y, Z
|
||||
from qiskit.test import QiskitTestCase
|
||||
from qiskit.circuit.qpy_serialization import dump, load
|
||||
from qiskit.quantum_info.random import random_unitary
|
||||
|
||||
|
||||
class TestLoadFromQPY(QiskitTestCase):
|
||||
|
@ -271,3 +277,200 @@ class TestLoadFromQPY(QiskitTestCase):
|
|||
qpy_file.seek(0)
|
||||
new_circs = load(qpy_file)
|
||||
self.assertEqual(circuits, new_circs)
|
||||
|
||||
def test_shared_bit_register(self):
|
||||
"""Test a circuit with shared bit registers."""
|
||||
qubits = [Qubit() for _ in range(5)]
|
||||
qc = QuantumCircuit()
|
||||
qc.add_bits(qubits)
|
||||
qr = QuantumRegister(bits=qubits)
|
||||
qc.add_register(qr)
|
||||
qc.h(qr)
|
||||
qc.cx(0, 1)
|
||||
qc.cx(0, 2)
|
||||
qc.cx(0, 3)
|
||||
qc.cx(0, 4)
|
||||
qc.measure_all()
|
||||
qpy_file = io.BytesIO()
|
||||
dump(qc, qpy_file)
|
||||
qpy_file.seek(0)
|
||||
new_qc = load(qpy_file)[0]
|
||||
self.assertEqual(qc, new_qc)
|
||||
|
||||
def test_hybrid_standalone_register(self):
|
||||
"""Test qpy serialization with registers that mix bit types"""
|
||||
qr = QuantumRegister(5, "foo")
|
||||
qr = QuantumRegister(name="bar", bits=qr[:3] + [Qubit(), Qubit()])
|
||||
cr = ClassicalRegister(5, "foo")
|
||||
cr = ClassicalRegister(name="classical_bar", bits=cr[:3] + [Clbit(), Clbit()])
|
||||
qc = QuantumCircuit(qr, cr)
|
||||
qc.h(0)
|
||||
qc.cx(0, 1)
|
||||
qc.cx(0, 2)
|
||||
qc.cx(0, 3)
|
||||
qc.cx(0, 4)
|
||||
qc.measure(qr, cr)
|
||||
qpy_file = io.BytesIO()
|
||||
dump(qc, qpy_file)
|
||||
qpy_file.seek(0)
|
||||
new_circ = load(qpy_file)[0]
|
||||
self.assertEqual(qc, new_circ)
|
||||
|
||||
def test_mixed_registers(self):
|
||||
"""Test circuit with mix of standalone and shared registers."""
|
||||
qubits = [Qubit() for _ in range(5)]
|
||||
clbits = [Clbit() for _ in range(5)]
|
||||
qc = QuantumCircuit()
|
||||
qc.add_bits(qubits)
|
||||
qc.add_bits(clbits)
|
||||
qr = QuantumRegister(bits=qubits)
|
||||
cr = ClassicalRegister(bits=clbits)
|
||||
qc.add_register(qr)
|
||||
qc.add_register(cr)
|
||||
qr_standalone = QuantumRegister(2, "standalone")
|
||||
qc.add_register(qr_standalone)
|
||||
cr_standalone = ClassicalRegister(2, "classical_standalone")
|
||||
qc.add_register(cr_standalone)
|
||||
qc.unitary(random_unitary(32, seed=42), qr)
|
||||
qc.unitary(random_unitary(4, seed=100), qr_standalone)
|
||||
qc.measure(qr, cr)
|
||||
qc.measure(qr_standalone, cr_standalone)
|
||||
qpy_file = io.BytesIO()
|
||||
dump(qc, qpy_file)
|
||||
qpy_file.seek(0)
|
||||
new_circ = load(qpy_file)[0]
|
||||
self.assertEqual(qc, new_circ)
|
||||
|
||||
def test_standalone_and_shared_out_of_order(self):
|
||||
"""Test circuit with register bits inserted out of order."""
|
||||
qr_standalone = QuantumRegister(2, "standalone")
|
||||
qubits = [Qubit() for _ in range(5)]
|
||||
clbits = [Clbit() for _ in range(5)]
|
||||
qc = QuantumCircuit()
|
||||
qc.add_bits(qubits)
|
||||
qc.add_bits(clbits)
|
||||
random.shuffle(qubits)
|
||||
random.shuffle(clbits)
|
||||
qr = QuantumRegister(bits=qubits)
|
||||
cr = ClassicalRegister(bits=clbits)
|
||||
qc.add_register(qr)
|
||||
qc.add_register(cr)
|
||||
qr_standalone = QuantumRegister(2, "standalone")
|
||||
cr_standalone = ClassicalRegister(2, "classical_standalone")
|
||||
qc.add_bits([qr_standalone[1], qr_standalone[0]])
|
||||
qc.add_bits([cr_standalone[1], cr_standalone[0]])
|
||||
qc.add_register(qr_standalone)
|
||||
qc.add_register(cr_standalone)
|
||||
qc.unitary(random_unitary(32, seed=42), qr)
|
||||
qc.unitary(random_unitary(4, seed=100), qr_standalone)
|
||||
qc.measure(qr, cr)
|
||||
qc.measure(qr_standalone, cr_standalone)
|
||||
qpy_file = io.BytesIO()
|
||||
dump(qc, qpy_file)
|
||||
qpy_file.seek(0)
|
||||
new_circ = load(qpy_file)[0]
|
||||
self.assertEqual(qc, new_circ)
|
||||
|
||||
def test_unitary_gate_with_label(self):
|
||||
"""Test that numpy array parameters are correctly serialized with a label"""
|
||||
qc = QuantumCircuit(1)
|
||||
unitary = np.array([[0, 1], [1, 0]])
|
||||
unitary_gate = UnitaryGate(unitary, "My Special unitary")
|
||||
qc.append(unitary_gate, [0])
|
||||
qpy_file = io.BytesIO()
|
||||
dump(qc, qpy_file)
|
||||
qpy_file.seek(0)
|
||||
new_circ = load(qpy_file)[0]
|
||||
self.assertEqual(qc, new_circ)
|
||||
self.assertEqual([x[0].label for x in qc.data], [x[0].label for x in new_circ.data])
|
||||
|
||||
def test_opaque_gate_with_label(self):
|
||||
"""Test that custom opaque gate is correctly serialized with a label"""
|
||||
custom_gate = Gate("black_box", 1, [])
|
||||
custom_gate.label = "My Special Black Box"
|
||||
qc = QuantumCircuit(1)
|
||||
qc.append(custom_gate, [0])
|
||||
qpy_file = io.BytesIO()
|
||||
dump(qc, qpy_file)
|
||||
qpy_file.seek(0)
|
||||
new_circ = load(qpy_file)[0]
|
||||
self.assertEqual(qc, new_circ)
|
||||
self.assertEqual([x[0].label for x in qc.data], [x[0].label for x in new_circ.data])
|
||||
|
||||
def test_opaque_instruction_with_label(self):
|
||||
"""Test that custom opaque instruction is correctly serialized with a label"""
|
||||
custom_gate = Instruction("black_box", 1, 0, [])
|
||||
custom_gate.label = "My Special Black Box Instruction"
|
||||
qc = QuantumCircuit(1)
|
||||
qc.append(custom_gate, [0])
|
||||
qpy_file = io.BytesIO()
|
||||
dump(qc, qpy_file)
|
||||
qpy_file.seek(0)
|
||||
new_circ = load(qpy_file)[0]
|
||||
self.assertEqual(qc, new_circ)
|
||||
self.assertEqual([x[0].label for x in qc.data], [x[0].label for x in new_circ.data])
|
||||
|
||||
def test_custom_gate_with_label(self):
|
||||
"""Test that custom gate is correctly serialized with a label"""
|
||||
custom_gate = Gate("black_box", 1, [])
|
||||
custom_definition = QuantumCircuit(1)
|
||||
custom_definition.h(0)
|
||||
custom_definition.rz(1.5, 0)
|
||||
custom_definition.sdg(0)
|
||||
custom_gate.definition = custom_definition
|
||||
custom_gate.label = "My special black box with a definition"
|
||||
|
||||
qc = QuantumCircuit(1)
|
||||
qc.append(custom_gate, [0])
|
||||
qpy_file = io.BytesIO()
|
||||
dump(qc, qpy_file)
|
||||
qpy_file.seek(0)
|
||||
new_circ = load(qpy_file)[0]
|
||||
self.assertEqual(qc, new_circ)
|
||||
self.assertEqual(qc.decompose(), new_circ.decompose())
|
||||
self.assertEqual([x[0].label for x in qc.data], [x[0].label for x in new_circ.data])
|
||||
|
||||
def test_custom_instruction_with_label(self):
|
||||
"""Test that custom instruction is correctly serialized with a label"""
|
||||
custom_gate = Instruction("black_box", 1, 0, [])
|
||||
custom_definition = QuantumCircuit(1)
|
||||
custom_definition.h(0)
|
||||
custom_definition.rz(1.5, 0)
|
||||
custom_definition.sdg(0)
|
||||
custom_gate.definition = custom_definition
|
||||
custom_gate.label = "My Special Black Box Instruction with a definition"
|
||||
qc = QuantumCircuit(1)
|
||||
qc.append(custom_gate, [0])
|
||||
qpy_file = io.BytesIO()
|
||||
dump(qc, qpy_file)
|
||||
qpy_file.seek(0)
|
||||
new_circ = load(qpy_file)[0]
|
||||
self.assertEqual(qc, new_circ)
|
||||
self.assertEqual(qc.decompose(), new_circ.decompose())
|
||||
self.assertEqual([x[0].label for x in qc.data], [x[0].label for x in new_circ.data])
|
||||
|
||||
def test_standard_gate_with_label(self):
|
||||
"""Test a standard gate with a label."""
|
||||
qc = QuantumCircuit(1)
|
||||
gate = XGate()
|
||||
gate.label = "My special X gate"
|
||||
qc.append(gate, [0])
|
||||
qpy_file = io.BytesIO()
|
||||
dump(qc, qpy_file)
|
||||
qpy_file.seek(0)
|
||||
new_circ = load(qpy_file)[0]
|
||||
self.assertEqual(qc, new_circ)
|
||||
self.assertEqual([x[0].label for x in qc.data], [x[0].label for x in new_circ.data])
|
||||
|
||||
def test_circuit_with_conditional_with_label(self):
|
||||
"""Test that instructions with conditions are correctly serialized."""
|
||||
qc = QuantumCircuit(1, 1)
|
||||
gate = XGate(label="My conditional x gate")
|
||||
gate.c_if(qc.cregs[0], 1)
|
||||
qc.append(gate, [0])
|
||||
qpy_file = io.BytesIO()
|
||||
dump(qc, qpy_file)
|
||||
qpy_file.seek(0)
|
||||
new_circ = load(qpy_file)[0]
|
||||
self.assertEqual(qc, new_circ)
|
||||
self.assertEqual([x[0].label for x in qc.data], [x[0].label for x in new_circ.data])
|
||||
|
|
Loading…
Reference in New Issue