Skip to content

Commit

Permalink
BackendV2 support for old device API (#514)
Browse files Browse the repository at this point in the history
* BackendV2 support for old device API. 

* Update CHANGELOG.md

Co-authored-by: soranjh <[email protected]>

---------

Co-authored-by: soranjh <[email protected]>
  • Loading branch information
austingmhuang and soranjh authored Apr 19, 2024
1 parent e3f9482 commit a2e56b5
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 3 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
device, ``BasicSimulator``, as the backend.
[(#493)](https://github.com/PennyLaneAI/pennylane-qiskit/pull/493)

* Backwards compatibility with Qiskit BackendV2 has now been implemented. Previously, only backends of type
BackendV1 were supported but now users can choose to use BackendV2 as well.
[(#514)](https://github.com/PennyLaneAI/pennylane-qiskit/pull/514)

### Improvements 🛠

* Following updates to allow device compatibility with Qiskit 1.0, the version of `qiskit-ibm-runtime` is
Expand All @@ -30,6 +34,7 @@

This release contains contributions from (in alphabetical order):
Lillian M. A. Frederiksen
Austin Huang

---
# Release 0.35.1
Expand Down
9 changes: 6 additions & 3 deletions pennylane_qiskit/qiskit_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from qiskit.circuit import library as lib
from qiskit.compiler import transpile
from qiskit.converters import circuit_to_dag, dag_to_circuit
from qiskit.providers import Backend, QiskitBackendNotFoundError
from qiskit.providers import Backend, BackendV2, QiskitBackendNotFoundError

from pennylane import QubitDevice, DeviceError
from pennylane.measurements import SampleMP, CountsMP, ClassicalShadowMP, ShadowExpvalMP
Expand Down Expand Up @@ -181,8 +181,11 @@ def __init__(self, wires, provider, backend, shots=1024, **kwargs):
self._capabilities["returns_state"] = self._is_state_backend

# Perform validation against backend
backend_qubits = self.backend.configuration().n_qubits
# if the backend has a set number of qubits, ensure wires doesn't exceed (some simulators have n_qubits=None)
backend_qubits = (
backend.num_qubits
if isinstance(backend, BackendV2)
else self.backend.configuration().n_qubits
)
if backend_qubits and len(self.wires) > int(backend_qubits):
raise ValueError(f"Backend '{backend}' supports maximum {backend_qubits} wires")

Expand Down
100 changes: 100 additions & 0 deletions tests/test_qiskit_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,67 @@
from pennylane_qiskit import AerDevice
from pennylane_qiskit.qiskit_device import QiskitDevice
from qiskit_aer import noise
from qiskit.providers import BackendV1, BackendV2
from qiskit_ibm_runtime.fake_provider import FakeManila, FakeManilaV2
from unittest.mock import Mock
from qiskit_ibm_runtime.options import Options


class Configuration:
def __init__(self, n_qubits, backend_name):
self.n_qubits = n_qubits
self.backend_name = backend_name
self.noise_model = None
self.method = "placeholder"

def get(self, attribute, default=None):
return getattr(self, attribute, default)


class MockedBackend(BackendV2):
def __init__(self, num_qubits=10, name="mocked_backend"):
self._options = Configuration(num_qubits, name)
self._service = "SomeServiceProvider"
self.name = name
self._target = Mock()
self._target.num_qubits = num_qubits

def set_options(self, noise_model):
self.options.noise_model = noise_model

def _default_options(self):
return {}

def max_circuits(self):
return 10

def run(self, *args, **kwargs):
return None

@property
def target(self):
return self._target


class MockedBackendLegacy(BackendV1):
def __init__(self, num_qubits=10, name="mocked_backend_legacy"):
self._configuration = Configuration(num_qubits, backend_name=name)
self._service = "SomeServiceProvider"
self._options = self._default_options()

def configuration(self):
return self._configuration

def _default_options(self):
return {}

def run(self, *args, **kwargs):
return None

@property
def options(self):
return self._options


test_transpile_options = [
{},
Expand All @@ -13,6 +74,45 @@
]

test_device_options = [{}, {"optimization_level": 3}, {"optimization_level": 1}]
backend = MockedBackend()
legacy_backend = MockedBackendLegacy()


class TestSupportForV1andV2:
"""Tests compatibility with BackendV1 and BackendV2"""

@pytest.mark.parametrize(
"backend",
[
legacy_backend,
backend,
],
)
def test_v1_and_v2_mocked(self, backend):
"""Test that device initializes with no error mocked"""
dev = qml.device("qiskit.remote", wires=10, backend=backend, use_primitives=True)
assert dev._backend == backend

@pytest.mark.parametrize(
"backend",
[
FakeManila(),
FakeManilaV2(),
]
)
def test_v1_and_v2_manila(self, backend):
"""Test that device initializes with no error with V1 and V2 backends by Qiskit"""
dev = qml.device("qiskit.remote", wires=5, backend=backend, use_primitives=True)

@qml.qnode(dev)
def circuit(x):
qml.RX(x, wires=[0])
qml.CNOT(wires=[0, 1])
return qml.sample(qml.PauliZ(0))

res = circuit(np.pi/2)
assert(isinstance(res, np.ndarray))
assert(np.shape(res) == (1024,))


class TestProbabilities:
Expand Down

0 comments on commit a2e56b5

Please sign in to comment.