mirror of https://github.com/Qiskit/qiskit.git
2038 lines
75 KiB
Python
2038 lines
75 KiB
Python
# This code is part of Qiskit.
|
|
#
|
|
# (C) Copyright IBM 2020, 2023.
|
|
#
|
|
# This code is licensed under the Apache License, Version 2.0. You may
|
|
# obtain a copy of this license in the LICENSE.txt file in the root directory
|
|
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
|
|
#
|
|
# Any modifications or derivative works of this code must retain this
|
|
# copyright notice, and modified files need to carry a notice indicating
|
|
# that they have been altered from the originals.
|
|
|
|
|
|
"""Test cases for qpy serialization."""
|
|
|
|
import io
|
|
import json
|
|
import random
|
|
import unittest
|
|
|
|
import ddt
|
|
import numpy as np
|
|
|
|
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, pulse
|
|
from qiskit.circuit import CASE_DEFAULT, IfElseOp, WhileLoopOp, SwitchCaseOp
|
|
from qiskit.circuit.classical import expr, types
|
|
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,
|
|
CXGate,
|
|
RYGate,
|
|
QFT,
|
|
QAOAAnsatz,
|
|
PauliEvolutionGate,
|
|
DCXGate,
|
|
MCU1Gate,
|
|
MCXGate,
|
|
MCXGrayCode,
|
|
MCXRecursive,
|
|
MCXVChain,
|
|
UCRXGate,
|
|
UCRYGate,
|
|
UCRZGate,
|
|
UnitaryGate,
|
|
DiagonalGate,
|
|
)
|
|
from qiskit.circuit.annotated_operation import (
|
|
AnnotatedOperation,
|
|
InverseModifier,
|
|
ControlModifier,
|
|
PowerModifier,
|
|
)
|
|
from qiskit.circuit.instruction import Instruction
|
|
from qiskit.circuit.parameter import Parameter
|
|
from qiskit.circuit.parametervector import ParameterVector
|
|
from qiskit.synthesis import LieTrotter, SuzukiTrotter
|
|
from qiskit.qpy import dump, load, UnsupportedFeatureForVersion, QPY_COMPATIBILITY_VERSION
|
|
from qiskit.quantum_info import Pauli, SparsePauliOp, Clifford
|
|
from qiskit.quantum_info.random import random_unitary
|
|
from qiskit.circuit.controlledgate import ControlledGate
|
|
from qiskit.utils import optionals
|
|
from test import QiskitTestCase # pylint: disable=wrong-import-order
|
|
|
|
|
|
@ddt.ddt
|
|
class TestLoadFromQPY(QiskitTestCase):
|
|
"""Test qpy set of methods."""
|
|
|
|
def assertDeprecatedBitProperties(self, original, roundtripped):
|
|
"""Test that deprecated bit attributes are equal if they are set in the original circuit."""
|
|
owned_qubits = [
|
|
(a, b) for a, b in zip(original.qubits, roundtripped.qubits) if a._register is not None
|
|
]
|
|
if owned_qubits:
|
|
original_qubits, roundtripped_qubits = zip(*owned_qubits)
|
|
self.assertEqual(original_qubits, roundtripped_qubits)
|
|
owned_clbits = [
|
|
(a, b) for a, b in zip(original.clbits, roundtripped.clbits) if a._register is not None
|
|
]
|
|
if owned_clbits:
|
|
original_clbits, roundtripped_clbits = zip(*owned_clbits)
|
|
self.assertEqual(original_clbits, roundtripped_clbits)
|
|
|
|
def assertMinimalVarEqual(self, left, right):
|
|
"""Replacement for asserting `QuantumCircuit` equality for use in `Var` tests, for use while
|
|
the `DAGCircuit` does not yet allow full equality checks. This should be removed and the
|
|
tests changed to directly call `assertEqual` once possible.
|
|
|
|
This filters out instructions that have `QuantumCircuit` parameters in the data comparison
|
|
(such as control-flow ops), which need to be handled separately."""
|
|
self.assertEqual(list(left.iter_input_vars()), list(right.iter_input_vars()))
|
|
self.assertEqual(list(left.iter_declared_vars()), list(right.iter_declared_vars()))
|
|
self.assertEqual(list(left.iter_captured_vars()), list(right.iter_captured_vars()))
|
|
|
|
def filter_ops(data):
|
|
return [
|
|
ins
|
|
for ins in data
|
|
if not any(isinstance(x, QuantumCircuit) for x in ins.operation.params)
|
|
]
|
|
|
|
self.assertEqual(filter_ops(left.data), filter_ops(right.data))
|
|
|
|
def test_qpy_full_path(self):
|
|
"""Test full path qpy serialization for basic circuit."""
|
|
qr_a = QuantumRegister(4, "a")
|
|
qr_b = QuantumRegister(4, "b")
|
|
cr_c = ClassicalRegister(4, "c")
|
|
cr_d = ClassicalRegister(4, "d")
|
|
q_circuit = QuantumCircuit(
|
|
qr_a,
|
|
qr_b,
|
|
cr_c,
|
|
cr_d,
|
|
name="MyCircuit",
|
|
metadata={"test": 1, "a": 2},
|
|
global_phase=3.14159,
|
|
)
|
|
q_circuit.h(qr_a)
|
|
q_circuit.cx(qr_a, qr_b)
|
|
q_circuit.barrier(qr_a)
|
|
q_circuit.barrier(qr_b)
|
|
q_circuit.measure(qr_a, cr_c)
|
|
q_circuit.measure(qr_b, cr_d)
|
|
qpy_file = io.BytesIO()
|
|
dump(q_circuit, qpy_file)
|
|
qpy_file.seek(0)
|
|
new_circ = load(qpy_file)[0]
|
|
self.assertEqual(q_circuit, new_circ)
|
|
self.assertEqual(q_circuit.global_phase, new_circ.global_phase)
|
|
self.assertEqual(q_circuit.metadata, new_circ.metadata)
|
|
self.assertEqual(q_circuit.name, new_circ.name)
|
|
self.assertDeprecatedBitProperties(q_circuit, new_circ)
|
|
|
|
def test_circuit_with_conditional(self):
|
|
"""Test that instructions with conditions are correctly serialized."""
|
|
qc = QuantumCircuit(1, 1)
|
|
with self.assertWarns(DeprecationWarning):
|
|
qc.x(0).c_if(qc.cregs[0], 1)
|
|
qpy_file = io.BytesIO()
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
with self.assertWarns(DeprecationWarning):
|
|
new_circ = load(qpy_file)[0]
|
|
self.assertEqual(qc, new_circ)
|
|
self.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_int_parameter(self):
|
|
"""Test that integer parameters are correctly serialized."""
|
|
qc = QuantumCircuit(1)
|
|
qc.rx(3, 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.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_float_parameter(self):
|
|
"""Test that float parameters are correctly serialized."""
|
|
qc = QuantumCircuit(1)
|
|
qc.rx(3.14, 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.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_numpy_float_parameter(self):
|
|
"""Test that numpy float parameters are correctly serialized."""
|
|
qc = QuantumCircuit(1)
|
|
qc.rx(np.float32(3.14), 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.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_numpy_int_parameter(self):
|
|
"""Test that numpy integer parameters are correctly serialized."""
|
|
qc = QuantumCircuit(1)
|
|
qc.rx(np.int16(3), 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.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_unitary_gate(self):
|
|
"""Test that numpy array parameters are correctly serialized"""
|
|
qc = QuantumCircuit(1)
|
|
unitary = np.array([[0, 1], [1, 0]])
|
|
qc.unitary(unitary, 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.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_controlled_unitary_gate(self):
|
|
"""Test that numpy array parameters are correctly serialized
|
|
in controlled unitary gate."""
|
|
qc = QuantumCircuit(2)
|
|
unitary = np.array([[0, 1], [1, 0]])
|
|
gate = UnitaryGate(unitary)
|
|
qc.append(gate.control(1), [0, 1])
|
|
|
|
with io.BytesIO() as qpy_file:
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
new_circ = load(qpy_file)[0]
|
|
|
|
self.assertEqual(qc.decompose(reps=5), new_circ.decompose(reps=5))
|
|
self.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_opaque_gate(self):
|
|
"""Test that custom opaque gate is correctly serialized"""
|
|
custom_gate = Gate("black_box", 1, [])
|
|
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.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_opaque_instruction(self):
|
|
"""Test that custom opaque instruction is correctly serialized"""
|
|
custom_gate = Instruction("black_box", 1, 0, [])
|
|
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.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_custom_gate(self):
|
|
"""Test that custom gate is correctly serialized"""
|
|
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
|
|
|
|
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.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_custom_instruction(self):
|
|
"""Test that custom instruction is correctly serialized"""
|
|
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
|
|
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.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_parameter(self):
|
|
"""Test that a circuit with a parameter is correctly serialized."""
|
|
theta = Parameter("theta")
|
|
qc = QuantumCircuit(5, 1)
|
|
qc.h(0)
|
|
for i in range(4):
|
|
qc.cx(i, i + 1)
|
|
|
|
qc.barrier()
|
|
qc.rz(theta, range(5))
|
|
qc.barrier()
|
|
for i in reversed(range(4)):
|
|
qc.cx(i, i + 1)
|
|
qc.h(0)
|
|
qc.measure(0, 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.assign_parameters({theta: 3.14}), new_circ.assign_parameters({theta: 3.14})
|
|
)
|
|
self.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_bound_parameter(self):
|
|
"""Test a circuit with a bound parameter is correctly serialized."""
|
|
theta = Parameter("theta")
|
|
qc = QuantumCircuit(5, 1)
|
|
qc.h(0)
|
|
for i in range(4):
|
|
qc.cx(i, i + 1)
|
|
|
|
qc.barrier()
|
|
qc.rz(theta, range(5))
|
|
qc.barrier()
|
|
for i in reversed(range(4)):
|
|
qc.cx(i, i + 1)
|
|
qc.h(0)
|
|
qc.measure(0, 0)
|
|
qc.assign_parameters({theta: 3.14})
|
|
|
|
qpy_file = io.BytesIO()
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
new_circ = load(qpy_file)[0]
|
|
self.assertEqual(qc, new_circ)
|
|
self.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_bound_calibration_parameter(self):
|
|
"""Test a circuit with a bound calibration parameter is correctly serialized.
|
|
|
|
In particular, this test ensures that parameters on a circuit
|
|
instruction are consistent with the circuit's calibrations dictionary
|
|
after serialization.
|
|
"""
|
|
amp = Parameter("amp")
|
|
|
|
with self.assertWarns(DeprecationWarning):
|
|
with pulse.builder.build() as sched:
|
|
pulse.builder.play(pulse.Constant(100, amp), pulse.DriveChannel(0))
|
|
|
|
gate = Gate("custom", 1, [amp])
|
|
|
|
qc = QuantumCircuit(1)
|
|
qc.append(gate, (0,))
|
|
with self.assertWarns(DeprecationWarning):
|
|
qc.add_calibration(gate, (0,), sched)
|
|
qc.assign_parameters({amp: 1 / 3}, inplace=True)
|
|
|
|
qpy_file = io.BytesIO()
|
|
with self.assertWarns(DeprecationWarning):
|
|
# qpy.dump warns for deprecations of pulse gate serialization
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
new_circ = load(qpy_file)[0]
|
|
self.assertEqual(qc, new_circ)
|
|
instruction = new_circ.data[0]
|
|
cal_key = (
|
|
tuple(new_circ.find_bit(q).index for q in instruction.qubits),
|
|
tuple(instruction.operation.params),
|
|
)
|
|
# Make sure that looking for a calibration based on the instruction's
|
|
# parameters succeeds
|
|
with self.assertWarns(DeprecationWarning):
|
|
self.assertIn(cal_key, new_circ.calibrations[gate.name])
|
|
|
|
def test_parameter_expression(self):
|
|
"""Test a circuit with a parameter expression."""
|
|
theta = Parameter("theta")
|
|
phi = Parameter("phi")
|
|
sum_param = theta + phi
|
|
qc = QuantumCircuit(5, 1)
|
|
qc.h(0)
|
|
for i in range(4):
|
|
qc.cx(i, i + 1)
|
|
|
|
qc.barrier()
|
|
qc.rz(sum_param, range(3))
|
|
qc.rz(phi, 3)
|
|
qc.rz(theta, 4)
|
|
qc.barrier()
|
|
for i in reversed(range(4)):
|
|
qc.cx(i, i + 1)
|
|
qc.h(0)
|
|
qc.measure(0, 0)
|
|
|
|
qpy_file = io.BytesIO()
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
new_circuit = load(qpy_file)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_string_parameter(self):
|
|
"""Test a PauliGate instruction that has string parameters."""
|
|
|
|
circ = QuantumCircuit(3)
|
|
circ.z(0)
|
|
circ.y(1)
|
|
circ.x(2)
|
|
|
|
qpy_file = io.BytesIO()
|
|
dump(circ, qpy_file)
|
|
qpy_file.seek(0)
|
|
new_circuit = load(qpy_file)[0]
|
|
self.assertEqual(circ, new_circuit)
|
|
self.assertDeprecatedBitProperties(circ, new_circuit)
|
|
|
|
def test_multiple_circuits(self):
|
|
"""Test multiple circuits can be serialized together."""
|
|
circuits = []
|
|
for i in range(10):
|
|
with self.assertWarns(DeprecationWarning):
|
|
circuits.append(
|
|
random_circuit(10, 10, measure=True, conditional=True, reset=True, seed=42 + i)
|
|
)
|
|
qpy_file = io.BytesIO()
|
|
dump(circuits, qpy_file)
|
|
qpy_file.seek(0)
|
|
with self.assertWarns(DeprecationWarning):
|
|
new_circs = load(qpy_file)
|
|
self.assertEqual(circuits, new_circs)
|
|
for old, new in zip(circuits, new_circs):
|
|
self.assertDeprecatedBitProperties(old, new)
|
|
|
|
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)
|
|
self.assertDeprecatedBitProperties(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)
|
|
self.assertDeprecatedBitProperties(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)
|
|
self.assertDeprecatedBitProperties(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)
|
|
self.assertDeprecatedBitProperties(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.operation.label for x in qc.data], [x.operation.label for x in new_circ.data]
|
|
)
|
|
self.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
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.operation.label for x in qc.data], [x.operation.label for x in new_circ.data]
|
|
)
|
|
self.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
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.operation.label for x in qc.data], [x.operation.label for x in new_circ.data]
|
|
)
|
|
self.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
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.operation.label for x in qc.data], [x.operation.label for x in new_circ.data]
|
|
)
|
|
self.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
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.operation.label for x in qc.data], [x.operation.label for x in new_circ.data]
|
|
)
|
|
self.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_custom_gate_with_noop_definition(self):
|
|
"""Test that a custom gate whose definition contains no elements is serialized with a
|
|
proper definition.
|
|
|
|
Regression test of gh-7429."""
|
|
empty = QuantumCircuit(1, name="empty").to_gate()
|
|
opaque = Gate("opaque", 1, [])
|
|
qc = QuantumCircuit(2)
|
|
qc.append(empty, [0], [])
|
|
qc.append(opaque, [1], [])
|
|
|
|
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(len(new_circ), 2)
|
|
self.assertIsInstance(new_circ.data[0].operation.definition, QuantumCircuit)
|
|
self.assertIs(new_circ.data[1].operation.definition, None)
|
|
self.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_custom_instruction_with_noop_definition(self):
|
|
"""Test that a custom instruction whose definition contains no elements is serialized with a
|
|
proper definition.
|
|
|
|
Regression test of gh-7429."""
|
|
empty = QuantumCircuit(1, name="empty").to_instruction()
|
|
opaque = Instruction("opaque", 1, 0, [])
|
|
qc = QuantumCircuit(2)
|
|
qc.append(empty, [0], [])
|
|
qc.append(opaque, [1], [])
|
|
|
|
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(len(new_circ), 2)
|
|
self.assertIsInstance(new_circ.data[0].operation.definition, QuantumCircuit)
|
|
self.assertIs(new_circ.data[1].operation.definition, None)
|
|
self.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_standard_gate_with_label(self):
|
|
"""Test a standard gate with a label."""
|
|
qc = QuantumCircuit(1)
|
|
gate = XGate(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.operation.label for x in qc.data], [x.operation.label for x in new_circ.data]
|
|
)
|
|
self.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
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")
|
|
with self.assertWarns(DeprecationWarning):
|
|
gate.c_if(qc.cregs[0], 1)
|
|
qc.append(gate, [0])
|
|
qpy_file = io.BytesIO()
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
with self.assertWarns(DeprecationWarning):
|
|
new_circ = load(qpy_file)[0]
|
|
self.assertEqual(qc, new_circ)
|
|
self.assertEqual(
|
|
[x.operation.label for x in qc.data], [x.operation.label for x in new_circ.data]
|
|
)
|
|
self.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_initialize_qft(self):
|
|
"""Test that initialize with a complex statevector and qft work."""
|
|
k = 5
|
|
state = (1 / np.sqrt(8)) * np.array(
|
|
[
|
|
np.exp(-1j * 2 * np.pi * k * (0) / 8),
|
|
np.exp(-1j * 2 * np.pi * k * (1) / 8),
|
|
np.exp(-1j * 2 * np.pi * k * (2) / 8),
|
|
np.exp(-1j * 2 * np.pi * k * 3 / 8),
|
|
np.exp(-1j * 2 * np.pi * k * 4 / 8),
|
|
np.exp(-1j * 2 * np.pi * k * 5 / 8),
|
|
np.exp(-1j * 2 * np.pi * k * 6 / 8),
|
|
np.exp(-1j * 2 * np.pi * k * 7 / 8),
|
|
]
|
|
)
|
|
|
|
qubits = 3
|
|
qc = QuantumCircuit(qubits, qubits)
|
|
qc.initialize(state)
|
|
qc.append(QFT(qubits), range(qubits))
|
|
qc.measure(range(qubits), range(qubits))
|
|
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.operation.label for x in qc.data], [x.operation.label for x in new_circ.data]
|
|
)
|
|
self.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_statepreparation(self):
|
|
"""Test that state preparation with a complex statevector and qft work."""
|
|
k = 5
|
|
state = (1 / np.sqrt(8)) * np.array(
|
|
[
|
|
np.exp(-1j * 2 * np.pi * k * (0) / 8),
|
|
np.exp(-1j * 2 * np.pi * k * (1) / 8),
|
|
np.exp(-1j * 2 * np.pi * k * (2) / 8),
|
|
np.exp(-1j * 2 * np.pi * k * 3 / 8),
|
|
np.exp(-1j * 2 * np.pi * k * 4 / 8),
|
|
np.exp(-1j * 2 * np.pi * k * 5 / 8),
|
|
np.exp(-1j * 2 * np.pi * k * 6 / 8),
|
|
np.exp(-1j * 2 * np.pi * k * 7 / 8),
|
|
]
|
|
)
|
|
|
|
qubits = 3
|
|
qc = QuantumCircuit(qubits, qubits)
|
|
qc.prepare_state(state)
|
|
qc.append(QFT(qubits), range(qubits))
|
|
qc.measure(range(qubits), range(qubits))
|
|
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.operation.label for x in qc.data], [x.operation.label for x in new_circ.data]
|
|
)
|
|
self.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_single_bit_teleportation(self):
|
|
"""Test a teleportation circuit with single bit conditions."""
|
|
qr = QuantumRegister(1)
|
|
cr = ClassicalRegister(2, name="name")
|
|
qc = QuantumCircuit(qr, cr, name="Reset Test")
|
|
qc.x(0)
|
|
qc.measure(0, cr[0])
|
|
with self.assertWarns(DeprecationWarning):
|
|
qc.x(0).c_if(cr[0], 1)
|
|
qc.measure(0, cr[1])
|
|
qpy_file = io.BytesIO()
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
with self.assertWarns(DeprecationWarning):
|
|
new_circ = load(qpy_file)[0]
|
|
self.assertEqual(qc, new_circ)
|
|
self.assertEqual(
|
|
[x.operation.label for x in qc.data], [x.operation.label for x in new_circ.data]
|
|
)
|
|
self.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_qaoa(self):
|
|
"""Test loading a QAOA circuit works."""
|
|
cost_operator = Pauli("ZIIZ")
|
|
qaoa = QAOAAnsatz(cost_operator, reps=2)
|
|
|
|
qpy_file = io.BytesIO()
|
|
dump(qaoa, qpy_file)
|
|
qpy_file.seek(0)
|
|
new_circ = load(qpy_file)[0]
|
|
self.assertEqual(qaoa, new_circ)
|
|
self.assertEqual(
|
|
[x.operation.label for x in qaoa.data], [x.operation.label for x in new_circ.data]
|
|
)
|
|
self.assertDeprecatedBitProperties(qaoa, new_circ)
|
|
|
|
def test_evolutiongate(self):
|
|
"""Test loading a circuit with evolution gate works."""
|
|
synthesis = LieTrotter(reps=2)
|
|
evo = PauliEvolutionGate(
|
|
SparsePauliOp.from_list([("ZI", 1), ("IZ", 1)]), time=2, synthesis=synthesis
|
|
)
|
|
|
|
qc = QuantumCircuit(2)
|
|
qc.append(evo, range(2))
|
|
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.operation.label for x in qc.data], [x.operation.label for x in new_circ.data]
|
|
)
|
|
|
|
new_evo = new_circ.data[0].operation
|
|
self.assertIsInstance(new_evo, PauliEvolutionGate)
|
|
self.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_evolutiongate_param_time(self):
|
|
"""Test loading a circuit with an evolution gate that has a parameter for time."""
|
|
synthesis = LieTrotter(reps=2)
|
|
time = Parameter("t")
|
|
evo = PauliEvolutionGate(
|
|
SparsePauliOp.from_list([("ZI", 1), ("IZ", 1)]), time=time, synthesis=synthesis
|
|
)
|
|
|
|
qc = QuantumCircuit(2)
|
|
qc.append(evo, range(2))
|
|
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.operation.label for x in qc.data], [x.operation.label for x in new_circ.data]
|
|
)
|
|
|
|
new_evo = new_circ.data[0].operation
|
|
self.assertIsInstance(new_evo, PauliEvolutionGate)
|
|
self.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_evolutiongate_param_expr_time(self):
|
|
"""Test loading a circuit with an evolution gate that has a parameter for time."""
|
|
synthesis = LieTrotter(reps=2)
|
|
time = Parameter("t")
|
|
evo = PauliEvolutionGate(
|
|
SparsePauliOp.from_list([("ZI", 1), ("IZ", 1)]), time=time * time, synthesis=synthesis
|
|
)
|
|
|
|
qc = QuantumCircuit(2)
|
|
qc.append(evo, range(2))
|
|
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.operation.label for x in qc.data], [x.operation.label for x in new_circ.data]
|
|
)
|
|
|
|
new_evo = new_circ.data[0].operation
|
|
self.assertIsInstance(new_evo, PauliEvolutionGate)
|
|
self.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_evolutiongate_param_vec_time(self):
|
|
"""Test loading a an evolution gate that has a param vector element for time."""
|
|
synthesis = LieTrotter(reps=2)
|
|
time = ParameterVector("TimeVec", 1)
|
|
evo = PauliEvolutionGate(
|
|
SparsePauliOp.from_list([("ZI", 1), ("IZ", 1)]), time=time[0], synthesis=synthesis
|
|
)
|
|
|
|
qc = QuantumCircuit(2)
|
|
qc.append(evo, range(2))
|
|
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.operation.label for x in qc.data], [x.operation.label for x in new_circ.data]
|
|
)
|
|
|
|
new_evo = new_circ.data[0].operation
|
|
self.assertIsInstance(new_evo, PauliEvolutionGate)
|
|
self.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_op_list_evolutiongate(self):
|
|
"""Test loading a circuit with evolution gate works."""
|
|
|
|
evo = PauliEvolutionGate(
|
|
[SparsePauliOp.from_list([("ZI", 1), ("IZ", 1)])] * 5, time=0.2, synthesis=None
|
|
)
|
|
qc = QuantumCircuit(2)
|
|
qc.append(evo, range(2))
|
|
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.operation.label for x in qc.data], [x.operation.label for x in new_circ.data]
|
|
)
|
|
|
|
new_evo = new_circ.data[0].operation
|
|
self.assertIsInstance(new_evo, PauliEvolutionGate)
|
|
self.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_op_evolution_gate_suzuki_trotter(self):
|
|
"""Test qpy path with a suzuki trotter synthesis method on an evolution gate."""
|
|
synthesis = SuzukiTrotter()
|
|
evo = PauliEvolutionGate(
|
|
SparsePauliOp.from_list([("ZI", 1), ("IZ", 1)]), time=0.2, synthesis=synthesis
|
|
)
|
|
|
|
qc = QuantumCircuit(2)
|
|
qc.append(evo, range(2))
|
|
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.operation.label for x in qc.data], [x.operation.label for x in new_circ.data]
|
|
)
|
|
|
|
new_evo = new_circ.data[0].operation
|
|
self.assertIsInstance(new_evo, PauliEvolutionGate)
|
|
self.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_parameter_expression_global_phase(self):
|
|
"""Test a circuit with a parameter expression global_phase."""
|
|
theta = Parameter("theta")
|
|
phi = Parameter("phi")
|
|
sum_param = theta + phi
|
|
qc = QuantumCircuit(5, 1, global_phase=sum_param)
|
|
qc.h(0)
|
|
for i in range(4):
|
|
qc.cx(i, i + 1)
|
|
|
|
qc.barrier()
|
|
qc.rz(sum_param, range(3))
|
|
qc.rz(phi, 3)
|
|
qc.rz(theta, 4)
|
|
qc.barrier()
|
|
for i in reversed(range(4)):
|
|
qc.cx(i, i + 1)
|
|
qc.h(0)
|
|
qc.measure(0, 0)
|
|
|
|
qpy_file = io.BytesIO()
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
new_circuit = load(qpy_file)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_parameter_global_phase(self):
|
|
"""Test a circuit with a parameter expression global_phase."""
|
|
theta = Parameter("theta")
|
|
qc = QuantumCircuit(2, global_phase=theta)
|
|
qc.h(0)
|
|
qc.cx(0, 1)
|
|
qc.measure_all()
|
|
qpy_file = io.BytesIO()
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
new_circuit = load(qpy_file)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
|
|
def test_parameter_vector(self):
|
|
"""Test a circuit with a parameter vector for gate parameters."""
|
|
qc = QuantumCircuit(11)
|
|
input_params = ParameterVector("x_par", 11)
|
|
user_params = ParameterVector("θ_par", 11)
|
|
for i, param in enumerate(user_params):
|
|
qc.ry(param, i)
|
|
for i, param in enumerate(input_params):
|
|
qc.rz(param, i)
|
|
qpy_file = io.BytesIO()
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
new_circuit = load(qpy_file)[0]
|
|
expected_params = [x.name for x in qc.parameters]
|
|
self.assertEqual([x.name for x in new_circuit.parameters], expected_params)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_parameter_vector_element_in_expression(self):
|
|
"""Test a circuit with a parameter vector used in a parameter expression."""
|
|
qc = QuantumCircuit(7)
|
|
entanglement = [[i, i + 1] for i in range(7 - 1)]
|
|
input_params = ParameterVector("x_par", 14)
|
|
user_params = ParameterVector("\u03B8_par", 1)
|
|
|
|
for i in range(qc.num_qubits):
|
|
qc.ry(user_params[0], qc.qubits[i])
|
|
|
|
for source, target in entanglement:
|
|
qc.cz(qc.qubits[source], qc.qubits[target])
|
|
|
|
for i in range(qc.num_qubits):
|
|
qc.rz(-2 * input_params[2 * i + 1], qc.qubits[i])
|
|
qc.rx(-2 * input_params[2 * i], qc.qubits[i])
|
|
|
|
qpy_file = io.BytesIO()
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
new_circuit = load(qpy_file)[0]
|
|
expected_params = [x.name for x in qc.parameters]
|
|
self.assertEqual([x.name for x in new_circuit.parameters], expected_params)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_parameter_vector_incomplete_warns(self):
|
|
"""Test that qpy's deserialization warns if a ParameterVector isn't fully identical."""
|
|
vec = ParameterVector("test", 3)
|
|
qc = QuantumCircuit(1, name="fun")
|
|
qc.rx(vec[1], 0)
|
|
qpy_file = io.BytesIO()
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
with self.assertWarnsRegex(UserWarning, r"^The ParameterVector.*Elements 0, 2.*fun$"):
|
|
new_circuit = load(qpy_file)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_parameter_vector_global_phase(self):
|
|
"""Test that a circuit with a standalone ParameterVectorElement phase works."""
|
|
vec = ParameterVector("phase", 1)
|
|
qc = QuantumCircuit(1, global_phase=vec[0])
|
|
qpy_file = io.BytesIO()
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
new_circuit = load(qpy_file)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_custom_metadata_serializer_full_path(self):
|
|
"""Test that running with custom metadata serialization works."""
|
|
|
|
class CustomObject:
|
|
"""Custom string container object."""
|
|
|
|
def __init__(self, string):
|
|
self.string = string
|
|
|
|
def __eq__(self, other):
|
|
return self.string == other.string
|
|
|
|
class CustomSerializer(json.JSONEncoder):
|
|
"""Custom json encoder to handle CustomObject."""
|
|
|
|
def default(self, o):
|
|
if isinstance(o, CustomObject):
|
|
return {"__type__": "Custom", "value": o.string}
|
|
return json.JSONEncoder.default(self, o)
|
|
|
|
class CustomDeserializer(json.JSONDecoder):
|
|
"""Custom json decoder to handle CustomObject."""
|
|
|
|
def object_hook(self, o): # pylint: disable=invalid-name,method-hidden
|
|
"""Hook to override default decoder.
|
|
|
|
Normally specified as a kwarg on load() that overloads the
|
|
default decoder. Done here to avoid reimplementing the
|
|
decode method.
|
|
"""
|
|
if "__type__" in o:
|
|
obj_type = o["__type__"]
|
|
if obj_type == "Custom":
|
|
return CustomObject(o["value"])
|
|
return o
|
|
|
|
theta = Parameter("theta")
|
|
qc = QuantumCircuit(2, global_phase=theta)
|
|
qc.h(0)
|
|
qc.cx(0, 1)
|
|
qc.measure_all()
|
|
circuits = [qc, qc.copy()]
|
|
circuits[0].metadata = {"key": CustomObject("Circuit 1")}
|
|
circuits[1].metadata = {"key": CustomObject("Circuit 2")}
|
|
qpy_file = io.BytesIO()
|
|
dump(circuits, qpy_file, metadata_serializer=CustomSerializer)
|
|
qpy_file.seek(0)
|
|
new_circuits = load(qpy_file, metadata_deserializer=CustomDeserializer)
|
|
self.assertEqual(qc, new_circuits[0])
|
|
self.assertEqual(circuits[0].metadata["key"], CustomObject("Circuit 1"))
|
|
self.assertEqual(qc, new_circuits[1])
|
|
self.assertEqual(circuits[1].metadata["key"], CustomObject("Circuit 2"))
|
|
self.assertDeprecatedBitProperties(qc, new_circuits[0])
|
|
self.assertDeprecatedBitProperties(qc, new_circuits[1])
|
|
|
|
def test_qpy_with_ifelseop(self):
|
|
"""Test qpy serialization with an if block."""
|
|
qc = QuantumCircuit(2, 2)
|
|
qc.h(0)
|
|
qc.measure(0, 0)
|
|
with qc.if_test((qc.clbits[0], True)):
|
|
qc.x(1)
|
|
qc.measure(1, 1)
|
|
qpy_file = io.BytesIO()
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
new_circuit = load(qpy_file)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_qpy_with_ifelseop_with_else(self):
|
|
"""Test qpy serialization with an else block."""
|
|
qc = QuantumCircuit(2, 2)
|
|
qc.h(0)
|
|
qc.measure(0, 0)
|
|
with qc.if_test((qc.clbits[0], True)) as else_:
|
|
qc.x(1)
|
|
with else_:
|
|
qc.y(1)
|
|
qc.measure(1, 1)
|
|
qpy_file = io.BytesIO()
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
new_circuit = load(qpy_file)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_qpy_with_while_loop(self):
|
|
"""Test qpy serialization with a for loop."""
|
|
qc = QuantumCircuit(2, 1)
|
|
|
|
with qc.while_loop((qc.clbits[0], 0)):
|
|
qc.h(0)
|
|
qc.cx(0, 1)
|
|
qc.measure(0, 0)
|
|
qpy_file = io.BytesIO()
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
new_circuit = load(qpy_file)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_qpy_with_for_loop(self):
|
|
"""Test qpy serialization with a for loop."""
|
|
qc = QuantumCircuit(2, 1)
|
|
|
|
with qc.for_loop(range(5)):
|
|
qc.h(0)
|
|
qc.cx(0, 1)
|
|
qc.measure(0, 0)
|
|
with self.assertWarns(DeprecationWarning):
|
|
qc.break_loop().c_if(0, True)
|
|
qpy_file = io.BytesIO()
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
with self.assertWarns(DeprecationWarning):
|
|
new_circuit = load(qpy_file)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_qpy_with_for_loop_iterator(self):
|
|
"""Test qpy serialization with a for loop."""
|
|
qc = QuantumCircuit(2, 1)
|
|
|
|
with qc.for_loop(iter(range(5))):
|
|
qc.h(0)
|
|
qc.cx(0, 1)
|
|
qc.measure(0, 0)
|
|
with self.assertWarns(DeprecationWarning):
|
|
qc.break_loop().c_if(0, True)
|
|
qpy_file = io.BytesIO()
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
with self.assertWarns(DeprecationWarning):
|
|
new_circuit = load(qpy_file)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_qpy_clbit_switch(self):
|
|
"""Test QPY serialization for a switch statement with a Clbit target."""
|
|
case_t = QuantumCircuit(2, 1)
|
|
case_t.x(0)
|
|
case_f = QuantumCircuit(2, 1)
|
|
case_f.z(0)
|
|
qc = QuantumCircuit(2, 1)
|
|
qc.switch(0, [(True, case_t), (False, case_f)], qc.qubits, qc.clbits)
|
|
|
|
with io.BytesIO() as fptr:
|
|
dump(qc, fptr)
|
|
fptr.seek(0)
|
|
new_circuit = load(fptr)[0]
|
|
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_qpy_register_switch(self):
|
|
"""Test QPY serialization for a switch statement with a ClassicalRegister target."""
|
|
qreg = QuantumRegister(2, "q")
|
|
creg = ClassicalRegister(3, "c")
|
|
|
|
case_0 = QuantumCircuit(qreg, creg)
|
|
case_0.x(0)
|
|
case_1 = QuantumCircuit(qreg, creg)
|
|
case_1.z(1)
|
|
case_2 = QuantumCircuit(qreg, creg)
|
|
case_2.x(1)
|
|
|
|
qc = QuantumCircuit(qreg, creg)
|
|
qc.switch(creg, [(0, case_0), ((1, 2), case_1), ((3, 4, CASE_DEFAULT), case_2)], qreg, creg)
|
|
|
|
with io.BytesIO() as fptr:
|
|
dump(qc, fptr)
|
|
fptr.seek(0)
|
|
new_circuit = load(fptr)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_standalone_register_partial_bit_in_circuit(self):
|
|
"""Test qpy with only some bits from standalone register."""
|
|
qr = QuantumRegister(2)
|
|
qc = QuantumCircuit([qr[0]])
|
|
qc.x(0)
|
|
qpy_file = io.BytesIO()
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
new_circuit = load(qpy_file)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_nested_tuple_param(self):
|
|
"""Test qpy with an instruction that contains nested tuples."""
|
|
inst = Instruction("tuple_test", 1, 0, [((((0, 1), (0, 1)), 2, 3), ("A", "B", "C"))])
|
|
qc = QuantumCircuit(1)
|
|
qc.append(inst, [0])
|
|
qpy_file = io.BytesIO()
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
new_circuit = load(qpy_file)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_empty_tuple_param(self):
|
|
"""Test qpy with an instruction that contains an empty tuple."""
|
|
inst = Instruction("empty_tuple_test", 1, 0, [()])
|
|
qc = QuantumCircuit(1)
|
|
qc.append(inst, [0])
|
|
qpy_file = io.BytesIO()
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
new_circuit = load(qpy_file)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_ucr_gates(self):
|
|
"""Test qpy with UCRX, UCRY, and UCRZ gates."""
|
|
qc = QuantumCircuit(3)
|
|
angles = [0, 0, 0, -np.pi]
|
|
ucrx, ucry, ucrz = UCRXGate(angles), UCRYGate(angles), UCRZGate(angles)
|
|
qc.append(ucrz, [2, 0, 1])
|
|
qc.append(ucry, [1, 0, 2])
|
|
qc.append(ucrx, [0, 2, 1])
|
|
qc.measure_all()
|
|
qpy_file = io.BytesIO()
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
new_circuit = load(qpy_file)[0]
|
|
self.assertEqual(qc.decompose().decompose(), new_circuit.decompose().decompose())
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_controlled_gate(self):
|
|
"""Test a custom controlled gate."""
|
|
qc = QuantumCircuit(3)
|
|
controlled_gate = DCXGate().control(1)
|
|
qc.append(controlled_gate, [0, 1, 2])
|
|
qpy_file = io.BytesIO()
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
new_circuit = load(qpy_file)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_controlled_gate_open_controls(self):
|
|
"""Test a controlled gate with open controls round-trips exactly."""
|
|
qc = QuantumCircuit(3)
|
|
controlled_gate = DCXGate().control(1, ctrl_state=0)
|
|
qc.append(controlled_gate, [0, 1, 2])
|
|
qpy_file = io.BytesIO()
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
new_circuit = load(qpy_file)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_nested_controlled_gate(self):
|
|
"""Test a custom nested controlled gate."""
|
|
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
|
|
|
|
qc = QuantumCircuit(3)
|
|
qc.append(custom_gate, [0])
|
|
controlled_gate = custom_gate.control(2)
|
|
qc.append(controlled_gate, [0, 1, 2])
|
|
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.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_open_controlled_gate(self):
|
|
"""Test an open control is preserved across serialization."""
|
|
qc = QuantumCircuit(2)
|
|
qc.cx(0, 1, ctrl_state=0)
|
|
with io.BytesIO() as fd:
|
|
dump(qc, fd)
|
|
fd.seek(0)
|
|
new_circ = load(fd)[0]
|
|
self.assertEqual(qc, new_circ)
|
|
self.assertEqual(qc.data[0].operation.ctrl_state, new_circ.data[0].operation.ctrl_state)
|
|
self.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_standard_control_gates(self):
|
|
"""Test standard library controlled gates."""
|
|
qc = QuantumCircuit(6)
|
|
mcu1_gate = MCU1Gate(np.pi, 2)
|
|
mcx_gate = MCXGate(5)
|
|
mcx_gray_gate = MCXGrayCode(5)
|
|
mcx_recursive_gate = MCXRecursive(4)
|
|
mcx_vchain_gate = MCXVChain(3)
|
|
qc.append(mcu1_gate, [0, 2, 1])
|
|
qc.append(mcx_gate, list(range(0, 6)))
|
|
qc.append(mcx_gray_gate, list(range(0, 6)))
|
|
qc.append(mcx_recursive_gate, list(range(0, 5)))
|
|
qc.append(mcx_vchain_gate, list(range(0, 5)))
|
|
qc.mcp(np.pi, [0, 2], 1)
|
|
qc.mcx([0, 2], 1)
|
|
qc.measure_all()
|
|
qpy_file = io.BytesIO()
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
new_circuit = load(qpy_file)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_controlled_gate_subclass_custom_definition(self):
|
|
"""Test controlled gate with overloaded definition.
|
|
|
|
Reproduce from: https://github.com/Qiskit/qiskit-terra/issues/8794
|
|
"""
|
|
|
|
class CustomCXGate(ControlledGate):
|
|
"""Custom CX with overloaded _define."""
|
|
|
|
def __init__(self, label=None, ctrl_state=None):
|
|
super().__init__(
|
|
"cx", 2, [], label, num_ctrl_qubits=1, ctrl_state=ctrl_state, base_gate=XGate()
|
|
)
|
|
|
|
def _define(self) -> None:
|
|
qc = QuantumCircuit(2, name=self.name)
|
|
qc.cx(0, 1)
|
|
self.definition = qc
|
|
|
|
qc = QuantumCircuit(2)
|
|
qc.append(CustomCXGate(), [0, 1])
|
|
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.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_multiple_controlled_gates(self):
|
|
"""Test multiple controlled gates with same name but different
|
|
parameter values.
|
|
|
|
Reproduce from: https://github.com/Qiskit/qiskit-terra/issues/10735
|
|
"""
|
|
|
|
qc = QuantumCircuit(3)
|
|
for i in range(3):
|
|
c2ry = RYGate(i + 1).control(2)
|
|
qc.append(c2ry, [i % 3, (i + 1) % 3, (i + 2) % 3])
|
|
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.assertDeprecatedBitProperties(qc, new_circ)
|
|
|
|
def test_load_with_loose_bits(self):
|
|
"""Test that loading from a circuit with loose bits works."""
|
|
qc = QuantumCircuit([Qubit(), Qubit(), Clbit()])
|
|
qpy_file = io.BytesIO()
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
new_circuit = load(qpy_file)[0]
|
|
self.assertEqual(tuple(new_circuit.qregs), ())
|
|
self.assertEqual(tuple(new_circuit.cregs), ())
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_load_with_loose_bits_and_registers(self):
|
|
"""Test that loading from a circuit with loose bits and registers works."""
|
|
qc = QuantumCircuit(QuantumRegister(3), ClassicalRegister(1), [Clbit()])
|
|
qpy_file = io.BytesIO()
|
|
dump(qc, qpy_file)
|
|
qpy_file.seek(0)
|
|
new_circuit = load(qpy_file)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_registers_after_loose_bits(self):
|
|
"""Test that a circuit whose registers appear after some loose bits roundtrips. Regression
|
|
test of gh-9094."""
|
|
qc = QuantumCircuit()
|
|
qc.add_bits([Qubit(), Clbit()])
|
|
qc.add_register(QuantumRegister(2, name="q1"))
|
|
qc.add_register(ClassicalRegister(2, name="c1"))
|
|
with io.BytesIO() as fptr:
|
|
dump(qc, fptr)
|
|
fptr.seek(0)
|
|
new_circuit = load(fptr)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_roundtrip_empty_register(self):
|
|
"""Test that empty registers round-trip correctly."""
|
|
qc = QuantumCircuit(QuantumRegister(0), ClassicalRegister(0))
|
|
with io.BytesIO() as fptr:
|
|
dump(qc, fptr)
|
|
fptr.seek(0)
|
|
new_circuit = load(fptr)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertEqual(qc.qregs, new_circuit.qregs)
|
|
self.assertEqual(qc.cregs, new_circuit.cregs)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_roundtrip_several_empty_registers(self):
|
|
"""Test that several empty registers round-trip correctly."""
|
|
qc = QuantumCircuit(
|
|
QuantumRegister(0, "a"),
|
|
QuantumRegister(0, "b"),
|
|
ClassicalRegister(0, "c"),
|
|
ClassicalRegister(0, "d"),
|
|
)
|
|
with io.BytesIO() as fptr:
|
|
dump(qc, fptr)
|
|
fptr.seek(0)
|
|
new_circuit = load(fptr)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertEqual(qc.qregs, new_circuit.qregs)
|
|
self.assertEqual(qc.cregs, new_circuit.cregs)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_roundtrip_empty_registers_with_loose_bits(self):
|
|
"""Test that empty registers still round-trip correctly in the presence of loose bits."""
|
|
loose = [Qubit(), Clbit()]
|
|
|
|
qc = QuantumCircuit(loose, QuantumRegister(0), ClassicalRegister(0))
|
|
with io.BytesIO() as fptr:
|
|
dump(qc, fptr)
|
|
fptr.seek(0)
|
|
new_circuit = load(fptr)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertEqual(qc.qregs, new_circuit.qregs)
|
|
self.assertEqual(qc.cregs, new_circuit.cregs)
|
|
|
|
qc = QuantumCircuit(QuantumRegister(0), ClassicalRegister(0), loose)
|
|
with io.BytesIO() as fptr:
|
|
dump(qc, fptr)
|
|
fptr.seek(0)
|
|
new_circuit = load(fptr)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertEqual(qc.qregs, new_circuit.qregs)
|
|
self.assertEqual(qc.cregs, new_circuit.cregs)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_incomplete_owned_bits(self):
|
|
"""Test that a circuit that contains only some bits that are owned by a register are
|
|
correctly roundtripped."""
|
|
reg = QuantumRegister(5, "q")
|
|
qc = QuantumCircuit(reg[:3])
|
|
qc.ccx(0, 1, 2)
|
|
with io.BytesIO() as fptr:
|
|
dump(qc, fptr)
|
|
fptr.seek(0)
|
|
new_circuit = load(fptr)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_diagonal_gate(self):
|
|
"""Test that a `DiagonalGate` successfully roundtrips."""
|
|
diag = DiagonalGate([1, -1, -1, 1])
|
|
|
|
qc = QuantumCircuit(2)
|
|
qc.append(diag, [0, 1])
|
|
|
|
with io.BytesIO() as fptr:
|
|
dump(qc, fptr)
|
|
fptr.seek(0)
|
|
new_circuit = load(fptr)[0]
|
|
# DiagonalGate (and some of the qiskit.circuit.library gates) have non-deterministic
|
|
# definitions with regard to internal instruction names, so cannot be directly compared for
|
|
# equality.
|
|
self.assertIs(type(qc.data[0].operation), type(new_circuit.data[0].operation))
|
|
self.assertEqual(qc.data[0].operation.params, new_circuit.data[0].operation.params)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
@ddt.data(QuantumCircuit.if_test, QuantumCircuit.while_loop)
|
|
def test_if_else_while_expr_simple(self, control_flow):
|
|
"""Test that `IfElseOp` and `WhileLoopOp` can have an `Expr` node as their `condition`, and
|
|
that this round-trips through QPY."""
|
|
body = QuantumCircuit(1)
|
|
qr = QuantumRegister(2, "q1")
|
|
cr = ClassicalRegister(2, "c1")
|
|
qc = QuantumCircuit(qr, cr)
|
|
control_flow(qc, expr.equal(cr, 3), body.copy(), [0], [])
|
|
control_flow(qc, expr.lift(qc.clbits[0]), body.copy(), [0], [])
|
|
with io.BytesIO() as fptr:
|
|
dump(qc, fptr)
|
|
fptr.seek(0)
|
|
new_circuit = load(fptr)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertEqual(qc.qregs, new_circuit.qregs)
|
|
self.assertEqual(qc.cregs, new_circuit.cregs)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
@ddt.data(QuantumCircuit.if_test, QuantumCircuit.while_loop)
|
|
def test_if_else_while_expr_nested(self, control_flow):
|
|
"""Test that `IfElseOp` and `WhileLoopOp` can have an `Expr` node as their `condition`, and
|
|
that this round-trips through QPY."""
|
|
inner = QuantumCircuit(1)
|
|
outer = QuantumCircuit(1, 1)
|
|
control_flow(outer, expr.lift(outer.clbits[0]), inner.copy(), [0], [])
|
|
|
|
qr = QuantumRegister(2, "q1")
|
|
cr = ClassicalRegister(2, "c1")
|
|
qc = QuantumCircuit(qr, cr)
|
|
control_flow(qc, expr.equal(cr, 3), outer.copy(), [1], [1])
|
|
with io.BytesIO() as fptr:
|
|
dump(qc, fptr)
|
|
fptr.seek(0)
|
|
new_circuit = load(fptr)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertEqual(qc.qregs, new_circuit.qregs)
|
|
self.assertEqual(qc.cregs, new_circuit.cregs)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_if_else_expr_stress(self):
|
|
"""Stress-test the `Expr` handling in the condition of an `IfElseOp`. This should hit on
|
|
every aspect of the `Expr` tree."""
|
|
inner = QuantumCircuit(1)
|
|
inner.x(0)
|
|
|
|
outer = QuantumCircuit(1, 1)
|
|
outer.if_test(expr.cast(outer.clbits[0], types.Bool()), inner.copy(), [0], [])
|
|
|
|
# Register whose size is deliberately larger that one byte.
|
|
cr1 = ClassicalRegister(256, "c1")
|
|
cr2 = ClassicalRegister(4, "c2")
|
|
loose = Clbit()
|
|
qc = QuantumCircuit([Qubit(), Qubit(), loose], cr1, cr2)
|
|
qc.rz(1.0, 0)
|
|
qc.if_test(
|
|
expr.logic_and(
|
|
expr.logic_and(
|
|
expr.logic_or(
|
|
expr.cast(
|
|
expr.less(expr.bit_and(cr1, 0x0F), expr.bit_not(cr1)),
|
|
types.Bool(),
|
|
),
|
|
expr.cast(
|
|
expr.less_equal(expr.bit_or(cr2, 7), expr.bit_xor(cr2, 7)),
|
|
types.Bool(),
|
|
),
|
|
),
|
|
expr.logic_and(
|
|
expr.logic_or(expr.equal(cr2, 2), expr.logic_not(expr.not_equal(cr2, 3))),
|
|
expr.logic_or(
|
|
expr.greater(cr2, 3),
|
|
expr.greater_equal(cr2, 3),
|
|
),
|
|
),
|
|
),
|
|
expr.logic_not(loose),
|
|
),
|
|
outer.copy(),
|
|
[1],
|
|
[0],
|
|
)
|
|
qc.rz(1.0, 0)
|
|
with io.BytesIO() as fptr:
|
|
dump(qc, fptr)
|
|
fptr.seek(0)
|
|
new_circuit = load(fptr)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertEqual(qc.qregs, new_circuit.qregs)
|
|
self.assertEqual(qc.cregs, new_circuit.cregs)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_switch_expr_simple(self):
|
|
"""Test that `SwitchCaseOp` can have an `Expr` node as its `target`, and that this
|
|
round-trips through QPY."""
|
|
body = QuantumCircuit(1)
|
|
qr = QuantumRegister(2, "q1")
|
|
cr = ClassicalRegister(2, "c1")
|
|
qc = QuantumCircuit(qr, cr)
|
|
qc.switch(expr.bit_and(cr, 3), [(1, body.copy())], [0], [])
|
|
qc.switch(expr.logic_not(qc.clbits[0]), [(False, body.copy())], [0], [])
|
|
with io.BytesIO() as fptr:
|
|
dump(qc, fptr)
|
|
fptr.seek(0)
|
|
new_circuit = load(fptr)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertEqual(qc.qregs, new_circuit.qregs)
|
|
self.assertEqual(qc.cregs, new_circuit.cregs)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_switch_expr_nested(self):
|
|
"""Test that `SwitchCaseOp` can have an `Expr` node as its `target`, and that this
|
|
round-trips through QPY."""
|
|
inner = QuantumCircuit(1)
|
|
outer = QuantumCircuit(1, 1)
|
|
outer.switch(expr.lift(outer.clbits[0]), [(False, inner.copy())], [0], [])
|
|
|
|
qr = QuantumRegister(2, "q1")
|
|
cr = ClassicalRegister(2, "c1")
|
|
qc = QuantumCircuit(qr, cr)
|
|
qc.switch(expr.lift(cr), [(3, outer.copy())], [1], [1])
|
|
with io.BytesIO() as fptr:
|
|
dump(qc, fptr)
|
|
fptr.seek(0)
|
|
new_circuit = load(fptr)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertEqual(qc.qregs, new_circuit.qregs)
|
|
self.assertEqual(qc.cregs, new_circuit.cregs)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_switch_expr_stress(self):
|
|
"""Stress-test the `Expr` handling in the target of a `SwitchCaseOp`. This should hit on
|
|
every aspect of the `Expr` tree."""
|
|
inner = QuantumCircuit(1)
|
|
inner.x(0)
|
|
|
|
outer = QuantumCircuit(1, 1)
|
|
outer.switch(expr.cast(outer.clbits[0], types.Bool()), [(True, inner.copy())], [0], [])
|
|
|
|
# Register whose size is deliberately larger that one byte.
|
|
cr1 = ClassicalRegister(256, "c1")
|
|
cr2 = ClassicalRegister(4, "c2")
|
|
loose = Clbit()
|
|
qc = QuantumCircuit([Qubit(), Qubit(), loose], cr1, cr2)
|
|
qc.rz(1.0, 0)
|
|
qc.switch(
|
|
expr.logic_and(
|
|
expr.logic_and(
|
|
expr.logic_or(
|
|
expr.cast(
|
|
expr.less(expr.bit_and(cr1, 0x0F), expr.bit_not(cr1)),
|
|
types.Bool(),
|
|
),
|
|
expr.cast(
|
|
expr.less_equal(expr.bit_or(cr2, 7), expr.bit_xor(cr2, 7)),
|
|
types.Bool(),
|
|
),
|
|
),
|
|
expr.logic_and(
|
|
expr.logic_or(expr.equal(cr2, 2), expr.logic_not(expr.not_equal(cr2, 3))),
|
|
expr.logic_or(
|
|
expr.greater(cr2, 3),
|
|
expr.greater_equal(cr2, 3),
|
|
),
|
|
),
|
|
),
|
|
expr.logic_not(loose),
|
|
),
|
|
[(False, outer.copy())],
|
|
[1],
|
|
[0],
|
|
)
|
|
qc.rz(1.0, 0)
|
|
with io.BytesIO() as fptr:
|
|
dump(qc, fptr)
|
|
fptr.seek(0)
|
|
new_circuit = load(fptr)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertEqual(qc.qregs, new_circuit.qregs)
|
|
self.assertEqual(qc.cregs, new_circuit.cregs)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_multiple_nested_control_custom_definitions(self):
|
|
"""Test that circuits with multiple controlled custom gates that in turn depend on custom
|
|
gates can be exported successfully when there are several such gates in the outer circuit.
|
|
See gh-9746"""
|
|
inner_1 = QuantumCircuit(1, name="inner_1")
|
|
inner_1.x(0)
|
|
inner_2 = QuantumCircuit(1, name="inner_2")
|
|
inner_2.y(0)
|
|
|
|
outer_1 = QuantumCircuit(1, name="outer_1")
|
|
outer_1.append(inner_1.to_gate(), [0], [])
|
|
outer_2 = QuantumCircuit(1, name="outer_2")
|
|
outer_2.append(inner_2.to_gate(), [0], [])
|
|
|
|
qc = QuantumCircuit(2)
|
|
qc.append(outer_1.to_gate().control(1), [0, 1], [])
|
|
qc.append(outer_2.to_gate().control(1), [0, 1], [])
|
|
|
|
with io.BytesIO() as fptr:
|
|
dump(qc, fptr)
|
|
fptr.seek(0)
|
|
new_circuit = load(fptr)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
@ddt.data(0, "01", [1, 0, 0, 0])
|
|
def test_valid_circuit_with_initialize_instruction(self, param):
|
|
"""Tests that circuit that has initialize instruction can be saved and correctly retrieved"""
|
|
qc = QuantumCircuit(2)
|
|
qc.initialize(param, qc.qubits)
|
|
with io.BytesIO() as fptr:
|
|
dump(qc, fptr)
|
|
fptr.seek(0)
|
|
new_circuit = load(fptr)[0]
|
|
self.assertEqual(qc, new_circuit)
|
|
self.assertDeprecatedBitProperties(qc, new_circuit)
|
|
|
|
def test_clifford(self):
|
|
"""Test that circuits with Clifford operations can be saved and retrieved correctly."""
|
|
cliff1 = Clifford.from_dict(
|
|
{
|
|
"stabilizer": ["-IZX", "+ZYZ", "+ZII"],
|
|
"destabilizer": ["+ZIZ", "+ZXZ", "-XIX"],
|
|
}
|
|
)
|
|
cliff2 = Clifford.from_dict(
|
|
{
|
|
"stabilizer": ["+YX", "+ZZ"],
|
|
"destabilizer": ["+IZ", "+YI"],
|
|
}
|
|
)
|
|
|
|
circuit = QuantumCircuit(6, 1)
|
|
circuit.cx(0, 1)
|
|
circuit.append(cliff1, [2, 4, 5])
|
|
circuit.h(4)
|
|
circuit.append(cliff2, [3, 0])
|
|
|
|
with io.BytesIO() as fptr:
|
|
dump(circuit, fptr)
|
|
fptr.seek(0)
|
|
new_circuit = load(fptr)[0]
|
|
self.assertEqual(circuit, new_circuit)
|
|
|
|
def test_annotated_operations(self):
|
|
"""Test that circuits with annotated operations can be saved and retrieved correctly."""
|
|
op1 = AnnotatedOperation(
|
|
CXGate(), [InverseModifier(), ControlModifier(1), PowerModifier(1.4), InverseModifier()]
|
|
)
|
|
op2 = AnnotatedOperation(XGate(), InverseModifier())
|
|
|
|
circuit = QuantumCircuit(6, 1)
|
|
circuit.cx(0, 1)
|
|
circuit.append(op1, [0, 1, 2])
|
|
circuit.h(4)
|
|
circuit.append(op2, [1])
|
|
|
|
with io.BytesIO() as fptr:
|
|
dump(circuit, fptr)
|
|
fptr.seek(0)
|
|
new_circuit = load(fptr)[0]
|
|
self.assertEqual(circuit, new_circuit)
|
|
|
|
def test_annotated_operations_iterative(self):
|
|
"""Test that circuits with iterative annotated operations can be saved and
|
|
retrieved correctly.
|
|
"""
|
|
op = AnnotatedOperation(AnnotatedOperation(XGate(), InverseModifier()), ControlModifier(1))
|
|
circuit = QuantumCircuit(4)
|
|
circuit.h(0)
|
|
circuit.append(op, [0, 2])
|
|
circuit.cx(2, 3)
|
|
with io.BytesIO() as fptr:
|
|
dump(circuit, fptr)
|
|
fptr.seek(0)
|
|
new_circuit = load(fptr)[0]
|
|
self.assertEqual(circuit, new_circuit)
|
|
|
|
def test_load_empty_vars(self):
|
|
"""Test loading empty circuits with variables."""
|
|
a = expr.Var.new("a", types.Bool())
|
|
b = expr.Var.new("b", types.Uint(8))
|
|
all_vars = {
|
|
a: expr.lift(False),
|
|
b: expr.lift(3, type=b.type),
|
|
expr.Var.new("θψφ", types.Bool()): expr.logic_not(a),
|
|
expr.Var.new("🐍🐍🐍", types.Uint(8)): expr.bit_and(b, b),
|
|
}
|
|
|
|
inputs = QuantumCircuit(inputs=list(all_vars))
|
|
with io.BytesIO() as fptr:
|
|
dump(inputs, fptr)
|
|
fptr.seek(0)
|
|
new_inputs = load(fptr)[0]
|
|
self.assertMinimalVarEqual(inputs, new_inputs)
|
|
self.assertDeprecatedBitProperties(inputs, new_inputs)
|
|
|
|
# Reversed order just to check there's no sorting shenanigans.
|
|
captures = QuantumCircuit(captures=list(all_vars)[::-1])
|
|
with io.BytesIO() as fptr:
|
|
dump(captures, fptr)
|
|
fptr.seek(0)
|
|
new_captures = load(fptr)[0]
|
|
self.assertMinimalVarEqual(captures, new_captures)
|
|
self.assertDeprecatedBitProperties(captures, new_captures)
|
|
|
|
declares = QuantumCircuit(declarations=all_vars)
|
|
with io.BytesIO() as fptr:
|
|
dump(declares, fptr)
|
|
fptr.seek(0)
|
|
new_declares = load(fptr)[0]
|
|
self.assertMinimalVarEqual(declares, new_declares)
|
|
self.assertDeprecatedBitProperties(declares, new_declares)
|
|
|
|
def test_load_empty_vars_if(self):
|
|
"""Test loading circuit with vars in if/else closures."""
|
|
a = expr.Var.new("a", types.Bool())
|
|
b = expr.Var.new("θψφ", types.Bool())
|
|
c = expr.Var.new("c", types.Uint(8))
|
|
d = expr.Var.new("🐍🐍🐍", types.Uint(8))
|
|
|
|
qc = QuantumCircuit(inputs=[a])
|
|
qc.add_var(b, expr.logic_not(a))
|
|
qc.add_var(c, expr.lift(0, c.type))
|
|
with qc.if_test(b) as else_:
|
|
qc.store(c, expr.lift(3, c.type))
|
|
with else_:
|
|
qc.add_var(d, expr.lift(7, d.type))
|
|
|
|
with io.BytesIO() as fptr:
|
|
dump(qc, fptr)
|
|
fptr.seek(0)
|
|
new_qc = load(fptr)[0]
|
|
self.assertMinimalVarEqual(qc, new_qc)
|
|
self.assertDeprecatedBitProperties(qc, new_qc)
|
|
|
|
old_if_else = qc.data[-1].operation
|
|
new_if_else = new_qc.data[-1].operation
|
|
# Sanity check for test.
|
|
self.assertIsInstance(old_if_else, IfElseOp)
|
|
self.assertIsInstance(new_if_else, IfElseOp)
|
|
self.assertEqual(len(old_if_else.blocks), len(new_if_else.blocks))
|
|
|
|
for old, new in zip(old_if_else.blocks, new_if_else.blocks):
|
|
self.assertMinimalVarEqual(old, new)
|
|
self.assertDeprecatedBitProperties(old, new)
|
|
|
|
def test_load_empty_vars_while(self):
|
|
"""Test loading circuit with vars in while closures."""
|
|
a = expr.Var.new("a", types.Bool())
|
|
b = expr.Var.new("θψφ", types.Bool())
|
|
c = expr.Var.new("🐍🐍🐍", types.Uint(8))
|
|
|
|
qc = QuantumCircuit(inputs=[a])
|
|
qc.add_var(b, expr.logic_not(a))
|
|
with qc.while_loop(b):
|
|
qc.add_var(c, expr.lift(7, c.type))
|
|
|
|
with io.BytesIO() as fptr:
|
|
dump(qc, fptr)
|
|
fptr.seek(0)
|
|
new_qc = load(fptr)[0]
|
|
self.assertMinimalVarEqual(qc, new_qc)
|
|
self.assertDeprecatedBitProperties(qc, new_qc)
|
|
|
|
old_while = qc.data[-1].operation
|
|
new_while = new_qc.data[-1].operation
|
|
# Sanity check for test.
|
|
self.assertIsInstance(old_while, WhileLoopOp)
|
|
self.assertIsInstance(new_while, WhileLoopOp)
|
|
self.assertEqual(len(old_while.blocks), len(new_while.blocks))
|
|
|
|
for old, new in zip(old_while.blocks, new_while.blocks):
|
|
self.assertMinimalVarEqual(old, new)
|
|
self.assertDeprecatedBitProperties(old, new)
|
|
|
|
def test_load_empty_vars_switch(self):
|
|
"""Test loading circuit with vars in switch closures."""
|
|
a = expr.Var.new("🐍🐍🐍", types.Uint(8))
|
|
|
|
qc = QuantumCircuit(1, 1, inputs=[a])
|
|
qc.measure(0, 0)
|
|
b_outer = qc.add_var("b", False)
|
|
with qc.switch(a) as case:
|
|
with case(0):
|
|
qc.store(b_outer, True)
|
|
with case(1):
|
|
qc.store(qc.clbits[0], False)
|
|
with case(2):
|
|
# Explicit shadowing.
|
|
qc.add_var("b", True)
|
|
with case(3):
|
|
qc.store(a, expr.lift(1, a.type))
|
|
with case(case.DEFAULT):
|
|
pass
|
|
|
|
with io.BytesIO() as fptr:
|
|
dump(qc, fptr)
|
|
fptr.seek(0)
|
|
new_qc = load(fptr)[0]
|
|
self.assertMinimalVarEqual(qc, new_qc)
|
|
self.assertDeprecatedBitProperties(qc, new_qc)
|
|
|
|
old_switch = qc.data[-1].operation
|
|
new_switch = new_qc.data[-1].operation
|
|
# Sanity check for test.
|
|
self.assertIsInstance(old_switch, SwitchCaseOp)
|
|
self.assertIsInstance(new_switch, SwitchCaseOp)
|
|
self.assertEqual(len(old_switch.blocks), len(new_switch.blocks))
|
|
|
|
for old, new in zip(old_switch.blocks, new_switch.blocks):
|
|
self.assertMinimalVarEqual(old, new)
|
|
self.assertDeprecatedBitProperties(old, new)
|
|
|
|
def test_roundtrip_index_expr(self):
|
|
"""Test that the `Index` node round-trips."""
|
|
a = expr.Var.new("a", types.Uint(8))
|
|
cr = ClassicalRegister(4, "cr")
|
|
qc = QuantumCircuit(cr, inputs=[a])
|
|
qc.store(expr.index(cr, 0), expr.index(a, a))
|
|
with io.BytesIO() as fptr:
|
|
dump(qc, fptr)
|
|
fptr.seek(0)
|
|
new_qc = load(fptr)[0]
|
|
self.assertEqual(qc, new_qc)
|
|
self.assertDeprecatedBitProperties(qc, new_qc)
|
|
|
|
def test_roundtrip_bitshift_expr(self):
|
|
"""Test that bit-shift expressions can round-trip."""
|
|
a = expr.Var.new("a", types.Uint(8))
|
|
cr = ClassicalRegister(4, "cr")
|
|
qc = QuantumCircuit(cr, inputs=[a])
|
|
with qc.if_test(expr.equal(expr.shift_right(expr.shift_left(a, 1), 1), a)):
|
|
pass
|
|
with io.BytesIO() as fptr:
|
|
dump(qc, fptr)
|
|
fptr.seek(0)
|
|
new_qc = load(fptr)[0]
|
|
self.assertEqual(qc, new_qc)
|
|
self.assertDeprecatedBitProperties(qc, new_qc)
|
|
|
|
@ddt.idata(range(QPY_COMPATIBILITY_VERSION, 12))
|
|
def test_pre_v12_rejects_standalone_var(self, version):
|
|
"""Test that dumping to older QPY versions rejects standalone vars."""
|
|
a = expr.Var.new("a", types.Bool())
|
|
qc = QuantumCircuit(inputs=[a])
|
|
with (
|
|
io.BytesIO() as fptr,
|
|
self.assertRaisesRegex(
|
|
UnsupportedFeatureForVersion, "version 12 is required.*realtime variables"
|
|
),
|
|
):
|
|
dump(qc, fptr, version=version)
|
|
|
|
@ddt.idata(range(QPY_COMPATIBILITY_VERSION, 12))
|
|
def test_pre_v12_rejects_index(self, version):
|
|
"""Test that dumping to older QPY versions rejects the `Index` node."""
|
|
# Be sure to use a register, since standalone vars would be rejected for other reasons.
|
|
qc = QuantumCircuit(ClassicalRegister(2, "cr"))
|
|
qc.store(expr.index(qc.cregs[0], 0), False)
|
|
with (
|
|
io.BytesIO() as fptr,
|
|
self.assertRaisesRegex(UnsupportedFeatureForVersion, "version 12 is required.*Index"),
|
|
):
|
|
dump(qc, fptr, version=version)
|
|
|
|
|
|
class TestSymengineLoadFromQPY(QiskitTestCase):
|
|
"""Test use of symengine in qpy set of methods."""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
|
|
theta = Parameter("theta")
|
|
phi = Parameter("phi")
|
|
sum_param = theta + phi
|
|
qc = QuantumCircuit(5, 1)
|
|
qc.h(0)
|
|
for i in range(4):
|
|
qc.cx(i, i + 1)
|
|
qc.barrier()
|
|
qc.rz(sum_param, range(3))
|
|
qc.rz(phi, 3)
|
|
qc.rz(theta, 4)
|
|
qc.barrier()
|
|
for i in reversed(range(4)):
|
|
qc.cx(i, i + 1)
|
|
qc.h(0)
|
|
qc.measure(0, 0)
|
|
|
|
self.qc = qc
|
|
|
|
def assertDeprecatedBitProperties(self, original, roundtripped):
|
|
"""Test that deprecated bit attributes are equal if they are set in the original circuit."""
|
|
owned_qubits = [
|
|
(a, b) for a, b in zip(original.qubits, roundtripped.qubits) if a._register is not None
|
|
]
|
|
if owned_qubits:
|
|
original_qubits, roundtripped_qubits = zip(*owned_qubits)
|
|
self.assertEqual(original_qubits, roundtripped_qubits)
|
|
owned_clbits = [
|
|
(a, b) for a, b in zip(original.clbits, roundtripped.clbits) if a._register is not None
|
|
]
|
|
if owned_clbits:
|
|
original_clbits, roundtripped_clbits = zip(*owned_clbits)
|
|
self.assertEqual(original_clbits, roundtripped_clbits)
|
|
|
|
@unittest.skipIf(not optionals.HAS_SYMENGINE, "Install symengine to run this test.")
|
|
def test_symengine_full_path(self):
|
|
"""Test use_symengine option for circuit with parameter expressions."""
|
|
qpy_file = io.BytesIO()
|
|
dump(self.qc, qpy_file, use_symengine=True)
|
|
qpy_file.seek(0)
|
|
new_circ = load(qpy_file)[0]
|
|
self.assertEqual(self.qc, new_circ)
|
|
self.assertDeprecatedBitProperties(self.qc, new_circ)
|