mirror of https://github.com/Qiskit/qiskit.git
205 lines
8.4 KiB
Python
205 lines
8.4 KiB
Python
# This code is part of Qiskit.
|
|
#
|
|
# (C) Copyright IBM 2021, 2024.
|
|
#
|
|
# 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.
|
|
|
|
# pylint: disable=missing-class-docstring,missing-function-docstring
|
|
# pylint: disable=missing-module-docstring
|
|
|
|
import math
|
|
|
|
from test import combine
|
|
|
|
from ddt import ddt, data
|
|
|
|
from numpy.testing import assert_array_max_ulp
|
|
from qiskit.circuit import QuantumCircuit, ClassicalRegister, QuantumRegister
|
|
from qiskit.circuit.library.standard_gates import (
|
|
CXGate,
|
|
ECRGate,
|
|
)
|
|
from qiskit.compiler import transpile
|
|
from qiskit.providers.basic_provider import BasicSimulator
|
|
from qiskit.providers.fake_provider import GenericBackendV2
|
|
from qiskit.quantum_info import Operator
|
|
from qiskit.transpiler import InstructionProperties
|
|
from test import QiskitTestCase # pylint: disable=wrong-import-order
|
|
|
|
from ..legacy_cmaps import TENERIFE_CMAP
|
|
from .fake_mumbai_v2 import FakeMumbaiFractionalCX
|
|
|
|
|
|
@ddt
|
|
class TestBackendV2(QiskitTestCase):
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.backend = GenericBackendV2(num_qubits=2, basis_gates=["rx", "u"], seed=42)
|
|
cx_props = {
|
|
(0, 1): InstructionProperties(duration=5.23e-7, error=0.00098115),
|
|
}
|
|
self.backend.target.add_instruction(CXGate(), cx_props)
|
|
ecr_props = {
|
|
(1, 0): InstructionProperties(duration=4.52e-9, error=0.0000132115),
|
|
}
|
|
self.backend.target.add_instruction(ECRGate(), ecr_props)
|
|
self.backend.options.set_validator("shots", (1, 4096))
|
|
|
|
def assertMatchesTargetConstraints(self, tqc, target):
|
|
qubit_indices = {qubit: index for index, qubit in enumerate(tqc.qubits)}
|
|
for instruction in tqc.data:
|
|
qubits = tuple(qubit_indices[x] for x in instruction.qubits)
|
|
target_set = target[instruction.operation.name].keys()
|
|
self.assertIn(
|
|
qubits,
|
|
target_set,
|
|
f"qargs: {qubits} not found in target for operation {instruction.operation.name}:"
|
|
f" {set(target_set)}",
|
|
)
|
|
|
|
def test_qubit_properties(self):
|
|
"""Test that qubit properties are returned as expected."""
|
|
props = self.backend.qubit_properties([1, 0])
|
|
assert_array_max_ulp([0.0001697368029059364, 0.00017739560485559633], [x.t1 for x in props])
|
|
assert_array_max_ulp(
|
|
[0.00010941773478876496, 0.00014388784397520525], [x.t2 for x in props]
|
|
)
|
|
assert_array_max_ulp([5487811175.818378, 5429298959.955691], [x.frequency for x in props])
|
|
|
|
def test_legacy_qubit_properties(self):
|
|
"""Test that qubit props work for backends not using properties in target."""
|
|
|
|
class FakeBackendV2LegacyQubitProps(GenericBackendV2):
|
|
"""Fake backend that doesn't use qubit properties via the target."""
|
|
|
|
def qubit_properties(self, qubit):
|
|
if isinstance(qubit, int):
|
|
return self.target.qubit_properties[qubit]
|
|
return [self.target.qubit_properties[i] for i in qubit]
|
|
|
|
props = FakeBackendV2LegacyQubitProps(num_qubits=2, seed=42).qubit_properties([1, 0])
|
|
assert_array_max_ulp([0.0001697368029059364, 0.00017739560485559633], [x.t1 for x in props])
|
|
assert_array_max_ulp(
|
|
[0.00010941773478876496, 0.00014388784397520525], [x.t2 for x in props]
|
|
)
|
|
assert_array_max_ulp([5487811175.818378, 5429298959.955691], [x.frequency for x in props])
|
|
|
|
def test_no_qubit_properties_raises(self):
|
|
"""Ensure that if no qubit properties are defined we raise correctly."""
|
|
with self.assertRaises(NotImplementedError):
|
|
BasicSimulator().qubit_properties(0)
|
|
|
|
def test_option_bounds(self):
|
|
"""Test that option bounds are enforced."""
|
|
with self.assertRaises(ValueError) as cm:
|
|
self.backend.set_options(shots=8192)
|
|
self.assertEqual(
|
|
str(cm.exception),
|
|
"Specified value for 'shots' is not a valid value, must be >=1 or <=4096",
|
|
)
|
|
|
|
@data(0, 1, 2, 3)
|
|
def test_transpile(self, opt_level):
|
|
"""Test that transpile() works with a BackendV2 backend."""
|
|
qc = QuantumCircuit(2)
|
|
qc.h(1)
|
|
qc.cz(1, 0)
|
|
tqc = transpile(qc, self.backend, optimization_level=opt_level)
|
|
self.assertTrue(Operator.from_circuit(tqc).equiv(qc))
|
|
self.assertMatchesTargetConstraints(tqc, self.backend.target)
|
|
|
|
@combine(
|
|
opt_level=[0, 1, 2, 3],
|
|
gate=["cx", "ecr", "cz"],
|
|
bidirectional=[True, False],
|
|
dsc=(
|
|
"Test GHZ circuit with {gate} using opt level {opt_level} on backend "
|
|
"with bidirectional={bidirectional}"
|
|
),
|
|
name="{gate}_level_{opt_level}_bidirectional_{bidirectional}",
|
|
)
|
|
def test_5q_ghz(self, opt_level, gate, bidirectional):
|
|
if bidirectional:
|
|
backend = GenericBackendV2(num_qubits=5, seed=42)
|
|
else:
|
|
backend = GenericBackendV2(num_qubits=5, coupling_map=TENERIFE_CMAP, seed=42)
|
|
qc = QuantumCircuit(5)
|
|
qc.h(0)
|
|
getattr(qc, gate)(0, 1)
|
|
getattr(qc, gate)(2, 1)
|
|
getattr(qc, gate)(2, 3)
|
|
getattr(qc, gate)(4, 3)
|
|
tqc = transpile(qc, backend, optimization_level=opt_level)
|
|
t_op = Operator.from_circuit(tqc)
|
|
self.assertTrue(t_op.equiv(qc))
|
|
self.assertMatchesTargetConstraints(tqc, backend.target)
|
|
|
|
def test_transpile_respects_arg_constraints(self):
|
|
"""Test that transpile() respects a heterogenous basis."""
|
|
# Test CX on wrong link
|
|
qc = QuantumCircuit(2)
|
|
qc.h(0)
|
|
qc.cx(1, 0)
|
|
tqc = transpile(qc, self.backend, optimization_level=1)
|
|
self.assertTrue(Operator.from_circuit(tqc).equiv(qc))
|
|
# Below is done to check we're decomposing cx(1, 0) with extra
|
|
# rotations to correct for direction. However because of fp
|
|
# differences between windows and other platforms the optimization
|
|
# from the 1q optimization passes differ and the output gates
|
|
# change (while still being equivalent). This relaxes the check to
|
|
# still ensure it's valid but not so specific that it fails on windows
|
|
self.assertEqual(tqc.count_ops().keys(), {"cx", "u"})
|
|
self.assertEqual(tqc.count_ops()["cx"], 1)
|
|
self.assertLessEqual(tqc.count_ops()["u"], 4)
|
|
self.assertMatchesTargetConstraints(tqc, self.backend.target)
|
|
# Test ECR on wrong link
|
|
qc = QuantumCircuit(2)
|
|
qc.h(0)
|
|
qc.ecr(0, 1)
|
|
tqc = transpile(qc, self.backend, optimization_level=1)
|
|
self.assertTrue(Operator.from_circuit(tqc).equiv(qc))
|
|
self.assertEqual(tqc.count_ops(), {"ecr": 1, "u": 4})
|
|
self.assertMatchesTargetConstraints(tqc, self.backend.target)
|
|
|
|
def test_transpile_relies_on_gate_direction(self):
|
|
"""Test that transpile() relies on gate direction pass for 2q."""
|
|
qc = QuantumCircuit(2)
|
|
qc.h(0)
|
|
qc.ecr(0, 1)
|
|
tqc = transpile(qc, self.backend, optimization_level=1)
|
|
expected = QuantumCircuit(2)
|
|
expected.u(0, 0, -math.pi, 0)
|
|
expected.u(math.pi / 2, 0, 0, 1)
|
|
expected.ecr(1, 0)
|
|
expected.u(math.pi / 2, 0, -math.pi, 0)
|
|
expected.u(math.pi / 2, 0, -math.pi, 1)
|
|
self.assertTrue(Operator.from_circuit(tqc).equiv(qc))
|
|
self.assertEqual(tqc.count_ops(), {"ecr": 1, "u": 4})
|
|
self.assertMatchesTargetConstraints(tqc, self.backend.target)
|
|
|
|
def test_transpile_mumbai_target(self):
|
|
"""Test that transpile respects a more involved target for a fake mumbai."""
|
|
backend = FakeMumbaiFractionalCX()
|
|
qc = QuantumCircuit(2)
|
|
qc.h(0)
|
|
qc.cx(1, 0)
|
|
qc.measure_all()
|
|
tqc = transpile(qc, backend, optimization_level=1)
|
|
qr = QuantumRegister(27, "q")
|
|
cr = ClassicalRegister(2, "meas")
|
|
expected = QuantumCircuit(qr, cr, global_phase=math.pi / 4)
|
|
expected.rz(math.pi / 2, 0)
|
|
expected.sx(0)
|
|
expected.rz(math.pi / 2, 0)
|
|
expected.cx(1, 0)
|
|
expected.barrier(qr[0], qr[1])
|
|
expected.measure(qr[0], cr[0])
|
|
expected.measure(qr[1], cr[1])
|
|
self.assertEqual(expected, tqc)
|