Skip to content

Commit

Permalink
Fixed bug in deepcopy of IBMBackend (#902)
Browse files Browse the repository at this point in the history
* Fixed infinite recursion when attempting to deepcopy an IBMBackend

* Moved test to integration tests

* Cleaned up deepcopy and relevant test

* Improved release notes

* Lint and Style

* black

* lint

---------

Co-authored-by: Kevin Tian <[email protected]>
  • Loading branch information
merav-aharoni and kt474 authored Jul 6, 2023
1 parent 2c02977 commit e6d48bc
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 1 deletion.
24 changes: 24 additions & 0 deletions qiskit_ibm_runtime/ibm_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

from typing import Iterable, Union, Optional, Any, List
from datetime import datetime as python_datetime
from copy import deepcopy

from qiskit import QuantumCircuit
from qiskit.qobj.utils import MeasLevel, MeasReturnType
Expand Down Expand Up @@ -199,6 +200,11 @@ def __getattr__(self, name: str) -> Any:
This magic method executes when user accesses an attribute that
does not yet exist on IBMBackend class.
"""
# Prevent recursion since these properties are accessed within __getattr__
if name in ["_properties", "_defaults", "_target", "_configuration"]:
raise AttributeError(
"'{}' object has no attribute '{}'".format(self.__class__.__name__, name)
)
# Lazy load properties and pulse defaults and construct the target object.
self._get_properties()
self._get_defaults()
Expand Down Expand Up @@ -529,6 +535,24 @@ def check_faulty(self, circuit: QuantumCircuit) -> None:
f"{instr} operating on a faulty edge {qubit_indices}"
)

def __deepcopy__(self, _memo: dict = None) -> "IBMBackend":
cpy = IBMBackend(
configuration=deepcopy(self.configuration()),
service=self._service,
api_client=deepcopy(self._api_client),
instance=self._instance,
)
cpy.name = self.name
cpy.description = self.description
cpy.online_date = self.online_date
cpy.backend_version = self.backend_version
cpy._coupling_map = self._coupling_map
cpy._defaults = deepcopy(self._defaults, _memo)
cpy._target = deepcopy(self._target, _memo)
cpy._max_circuits = self._max_circuits
cpy._options = deepcopy(self._options, _memo)
return cpy


class IBMRetiredBackend(IBMBackend):
"""Backend class interfacing with an IBM Quantum device no longer available."""
Expand Down
6 changes: 6 additions & 0 deletions releasenotes/notes/backend_deepcopy-965f6a19a6d17cb2.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
fixes:
- |
Fixed infinite recursion when attempting to deepcopy an IBMBackend. Added
a method :meth:`qiskit_ibm_runtime.IBMBackend.deepcopy`.
24 changes: 23 additions & 1 deletion test/integration/test_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"""Tests for backend functions using real runtime service."""

from unittest import SkipTest

import copy

from qiskit.transpiler.target import Target
from qiskit_ibm_runtime import QiskitRuntimeService
Expand Down Expand Up @@ -163,3 +163,25 @@ def test_backend_run(self):
with self.subTest(backend=backend.name):
with self.assertRaises(RuntimeError):
backend.run()

def test_backend_deepcopy(self):
"""Test that deepcopy on IBMBackend works correctly"""
backend = self.backend
with self.subTest(backend=backend.name):
backend_copy = copy.deepcopy(backend)
self.assertEqual(backend_copy.name, backend.name)
self.assertEqual(
backend_copy.configuration().basis_gates,
backend.configuration().basis_gates,
)
self.assertEqual(
backend_copy.properties().last_update_date,
backend.properties().last_update_date,
)
self.assertEqual(backend_copy._instance, backend._instance)
self.assertEqual(backend_copy._service._backends, backend._service._backends)
self.assertEqual(backend_copy._get_defaults(), backend._get_defaults())
self.assertEqual(
backend_copy._api_client._session.base_url,
backend._api_client._session.base_url,
)

0 comments on commit e6d48bc

Please sign in to comment.