From ecdf72fea93013a04c5e94680a9236c2c596ee19 Mon Sep 17 00:00:00 2001
From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com>
Date: Tue, 6 Jun 2023 15:49:03 +0100
Subject: [PATCH 01/10] Fix/tk to qiskit rendundancies (#115)

* don't use RemoveRedundancies in tk_to_qiskit

* add test

* update changelog

* black formatting

* fix changelog
---
 docs/changelog.rst                         | 3 +++
 pytket/extensions/qiskit/qiskit_convert.py | 5 +----
 tests/qiskit_convert_test.py               | 7 +++++++
 3 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/docs/changelog.rst b/docs/changelog.rst
index 386bfb96..7df2a39d 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -1,5 +1,8 @@
 Changelog
 ~~~~~~~~~
+0.40.0 (unreleased)
+-------------------
+* Fix to the `tk_to_qiskit` converter to prevent cancellation of redundant gates when converting to qiskit.
 
 0.39.0 (May 2023)
 -----------------
diff --git a/pytket/extensions/qiskit/qiskit_convert.py b/pytket/extensions/qiskit/qiskit_convert.py
index 638a6d54..ce2b07fa 100644
--- a/pytket/extensions/qiskit/qiskit_convert.py
+++ b/pytket/extensions/qiskit/qiskit_convert.py
@@ -74,7 +74,7 @@
 from pytket.architecture import Architecture, FullyConnected  # type: ignore
 from pytket.utils import QubitPauliOperator, gen_term_sequence_circuit
 
-from pytket.passes import RebaseCustom, RemoveRedundancies  # type: ignore
+from pytket.passes import RebaseCustom  # type: ignore
 
 if TYPE_CHECKING:
     from qiskit.providers.backend import BackendV1 as QiskitBackend  # type: ignore
@@ -654,9 +654,6 @@ def tk_to_qiskit(
     # Apply a rebase to the set of pytket gates which have replacements in qiskit
     supported_gate_rebase.apply(tkc)
 
-    # Remove redundant gate operations which could be introduced by the rebase
-    RemoveRedundancies().apply(tkc)
-
     for command in tkc:
         append_tk_command_to_qiskit(
             command.op, command.args, qcirc, qregmap, cregmap, symb_map, range_preds
diff --git a/tests/qiskit_convert_test.py b/tests/qiskit_convert_test.py
index 1334e0aa..7d2f4597 100644
--- a/tests/qiskit_convert_test.py
+++ b/tests/qiskit_convert_test.py
@@ -760,3 +760,10 @@ def test_qcontrolbox_conversion() -> None:
     tkc2 = qiskit_to_tk(qc2)
     assert tkc2.n_gates == 3
     assert tkc2.n_gates_of_type(OpType.QControlBox) == 3
+
+
+# Ensures that the tk_to_qiskit converter does not cancel redundant gates
+def test_tk_to_qiskit_redundancies() -> None:
+    h_circ = Circuit(1).H(0).H(0)
+    qc_h = tk_to_qiskit(h_circ)
+    assert qc_h.count_ops()["h"] == 2

From 74d9866a2e263b897139e20e0e4f6fe5f8a42e0c Mon Sep 17 00:00:00 2001
From: Alec Edgington <54802828+cqc-alec@users.noreply.github.com>
Date: Thu, 15 Jun 2023 15:52:02 +0100
Subject: [PATCH 02/10] [bugfix] Take account of control state for controlled
 gates in `qiskit_to_tk` (#118)

---
 docs/changelog.rst                         |  3 +++
 pytket/extensions/qiskit/qiskit_convert.py | 27 +++++++++++++++++++++
 tests/qiskit_convert_test.py               | 28 ++++++++++++++++++++++
 3 files changed, 58 insertions(+)

diff --git a/docs/changelog.rst b/docs/changelog.rst
index 7df2a39d..a875cbd1 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -1,8 +1,11 @@
 Changelog
 ~~~~~~~~~
+
 0.40.0 (unreleased)
 -------------------
+
 * Fix to the `tk_to_qiskit` converter to prevent cancellation of redundant gates when converting to qiskit.
+* Fix handling of control state in `qiskit_to_tk`.
 
 0.39.0 (May 2023)
 -----------------
diff --git a/pytket/extensions/qiskit/qiskit_convert.py b/pytket/extensions/qiskit/qiskit_convert.py
index ce2b07fa..3dc32614 100644
--- a/pytket/extensions/qiskit/qiskit_convert.py
+++ b/pytket/extensions/qiskit/qiskit_convert.py
@@ -273,6 +273,21 @@ def __init__(
     def circuit(self) -> Circuit:
         return self.tkc
 
+    def add_xs(
+        self,
+        num_ctrl_qubits: Optional[int],
+        ctrl_state: Optional[Union[str, int]],
+        qargs: List["Qubit"],
+    ) -> None:
+        if ctrl_state is not None:
+            assert isinstance(num_ctrl_qubits, int)
+            assert num_ctrl_qubits >= 0
+            c = int(ctrl_state, 2) if isinstance(ctrl_state, str) else int(ctrl_state)
+            assert c >= 0 and (c >> num_ctrl_qubits) == 0
+            for i in range(num_ctrl_qubits):
+                if ((c >> i) & 1) == 0:
+                    self.tkc.X(self.qbmap[qargs[i]])
+
     def add_qiskit_data(self, data: "QuantumCircuitData") -> None:
         for instr, qargs, cargs in data:
             condition_kwargs = {}
@@ -282,6 +297,15 @@ def add_qiskit_data(self, data: "QuantumCircuitData") -> None:
                     "condition_bits": [cond_reg[k] for k in range(len(cond_reg))],
                     "condition_value": instr.condition[1],
                 }
+            # Controlled operations may be controlled on values other than all-1. Handle
+            # this by prepending and appending X gates on the control qubits.
+            ctrl_state, num_ctrl_qubits = None, None
+            try:
+                ctrl_state = instr.ctrl_state
+                num_ctrl_qubits = instr.num_ctrl_qubits
+            except AttributeError:
+                pass
+            self.add_xs(num_ctrl_qubits, ctrl_state, qargs)
             optype = None
             if type(instr) == ControlledGate:
                 if type(instr.base_gate) == qiskit_gates.RYGate:
@@ -372,6 +396,8 @@ def add_qiskit_data(self, data: "QuantumCircuitData") -> None:
                 params = [param_to_tk(p) for p in instr.params]
                 self.tkc.add_gate(optype, params, qubits + bits, **condition_kwargs)
 
+            self.add_xs(num_ctrl_qubits, ctrl_state, qargs)
+
 
 def qiskit_to_tk(qcirc: QuantumCircuit, preserve_param_uuid: bool = False) -> Circuit:
     """
@@ -589,6 +615,7 @@ def append_tk_command_to_qiskit(
 
 Param = Union[float, "sympy.Expr"]  # Type for TK1 and U3 parameters
 
+
 # Use the U3 gate for tk1_replacement as this is a member of _supported_tket_gates
 def _tk1_to_u3(a: Param, b: Param, c: Param) -> Circuit:
     tk1_circ = Circuit(1)
diff --git a/tests/qiskit_convert_test.py b/tests/qiskit_convert_test.py
index 7d2f4597..58a2e0eb 100644
--- a/tests/qiskit_convert_test.py
+++ b/tests/qiskit_convert_test.py
@@ -767,3 +767,31 @@ def test_tk_to_qiskit_redundancies() -> None:
     h_circ = Circuit(1).H(0).H(0)
     qc_h = tk_to_qiskit(h_circ)
     assert qc_h.count_ops()["h"] == 2
+
+
+def test_ccx_conversion() -> None:
+    # https://github.com/CQCL/pytket-qiskit/issues/117
+    c00 = QuantumCircuit(3)
+    c00.ccx(0, 1, 2, 0)  # 0 = "00" (little-endian)
+    assert compare_unitaries(
+        qiskit_to_tk(c00).get_unitary(),
+        Circuit(3).X(0).X(1).CCX(0, 1, 2).X(0).X(1).get_unitary(),
+    )
+    c10 = QuantumCircuit(3)
+    c10.ccx(0, 1, 2, 1)  # 1 = "10" (little-endian)
+    assert compare_unitaries(
+        qiskit_to_tk(c10).get_unitary(),
+        Circuit(3).X(1).CCX(0, 1, 2).X(1).get_unitary(),
+    )
+    c01 = QuantumCircuit(3)
+    c01.ccx(0, 1, 2, 2)  # 2 = "01" (little-endian)
+    assert compare_unitaries(
+        qiskit_to_tk(c01).get_unitary(),
+        Circuit(3).X(0).CCX(0, 1, 2).X(0).get_unitary(),
+    )
+    c11 = QuantumCircuit(3)
+    c11.ccx(0, 1, 2, 3)  # 3 = "11" (little-endian)
+    assert compare_unitaries(
+        qiskit_to_tk(c11).get_unitary(),
+        Circuit(3).CCX(0, 1, 2).get_unitary(),
+    )

From 84ab81eb0476471f5a9648035348e14ec907fe1c Mon Sep 17 00:00:00 2001
From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com>
Date: Fri, 16 Jun 2023 13:56:12 +0100
Subject: [PATCH 03/10] Feature/ibm provider (#116)

* rewrite _save_ibmq_auth

* use IBMBackend

* use IBMProvdier instead of AccountProvider

* fix name

* IBMProvider type

* rewrite auth saving again

* use IBMProvider in conftest

* replace IBMQ in backend tests

* restore backend name

* update backend test requirements

* black

* handle instance instead of hub, group, project

* try to fix available devices test

* remote instances of AccountProvider

* fix provider args

* provider arg handling

* provider in tests

* IBMQEmualtor args

* remove remaining instances of hub, group, project args

* fix available devices test

* fix autopass test

* black

* black

* allow provider arg in available_devices

* available devices handling

* test without T1 times

* set package versions to qiskit==0.43.1 and ibm_prodier~=0.6 to resolve failing test

* use IBMProvider in TKET auto pass

* unused imports

* format

* fix autopass type and remove unecessary import

* linting

* imports

* another import

* use kwargs in available devices

* black

* use older package versions

* remove comments and change test error message

* update with error from IBMProvider

* format

* Optional for instance arg

* None value

* construct emulator noisemodel directly from backend

* docstring and formatting

* remove unused imports

* try to fix ci issues

* add type annotation and type: ignore(s) for mypy

* black formatting

* add one last type: ignore

* black

* fix ci issues

* remove comment

* try without additional token check

* add addtional token check back in

* update credentials documentation

* remove type: ignore

* use notimeout markers on tests

* black

* remove ovewrite flag from docs

* Change name of error message

* fix import error for IBMQCredentialsError

* update changelog

---------

Co-authored-by: Melf <cqc@melf.de>
Co-authored-by: cqc-melf <70640934+cqc-melf@users.noreply.github.com>
---
 docs/changelog.rst                            |   1 +
 docs/intro.txt                                |  18 +--
 pytket/extensions/qiskit/__init__.py          |   2 +-
 pytket/extensions/qiskit/backends/__init__.py |   2 +-
 pytket/extensions/qiskit/backends/config.py   |  20 +---
 pytket/extensions/qiskit/backends/ibm.py      | 109 +++++++-----------
 .../qiskit/backends/ibmq_emulator.py          |  18 +--
 pytket/extensions/qiskit/tket_pass.py         |  10 +-
 setup.py                                      |   1 +
 tests/backend_test.py                         |  58 +++-------
 tests/conftest.py                             |  45 ++++----
 tests/qiskit_backend_test.py                  |  13 +--
 tests/qiskit_convert_test.py                  |  16 +--
 13 files changed, 131 insertions(+), 182 deletions(-)

diff --git a/docs/changelog.rst b/docs/changelog.rst
index a875cbd1..067c9e6b 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -4,6 +4,7 @@ Changelog
 0.40.0 (unreleased)
 -------------------
 
+* IBM devices are now accessed using the `qiskit-ibm-provider <https://github.com/Qiskit/qiskit-ibm-provider>`_ instead of the deprecated :py:class:`IBMQ`. This allows the newest IBM devices and simulators to be accessed through the pytket-qiskit extension.
 * Fix to the `tk_to_qiskit` converter to prevent cancellation of redundant gates when converting to qiskit.
 * Fix handling of control state in `qiskit_to_tk`.
 
diff --git a/docs/intro.txt b/docs/intro.txt
index b742ac14..d7ea2ad5 100644
--- a/docs/intro.txt
+++ b/docs/intro.txt
@@ -50,7 +50,7 @@ The :py:class:`AerBackend` also supports GPU simulation which can be configured
 Access and Credentials
 ======================
 
-With the exception of the Aer simulators, accessing devices and simulators through the ``pytket-qiskit`` extension requires an IBMQ account. An account can be set up here: https://quantum-computing.ibm.com/login.
+With the exception of the Aer simulators, accessing devices and simulators through the ``pytket-qiskit`` extension requires an IBM account. An account can be set up here: https://quantum-computing.ibm.com/login.
 
 Once you have created an account you can obtain an API token which you can use to configure your credentials locally.
 
@@ -60,7 +60,7 @@ Once you have created an account you can obtain an API token which you can use t
     
     set_ibmq_config(ibmq_api_token=ibm_token)
 
-This will save your IBMQ credentials locally. After saving your credentials you can access ``pytket-qiskit`` backend repeatedly without having to re-initialise your credentials.
+After saving your credentials you can access ``pytket-qiskit`` backend repeatedly without having to re-initialise your credentials.
 
 If you are a member of an IBM hub then you can add this information to ``set_ibmq_config`` as well.
 
@@ -68,27 +68,29 @@ If you are a member of an IBM hub then you can add this information to ``set_ibm
 
     from pytket.extensions.qiskit import set_ibmq_config
 
-    set_ibmq_config(ibmq_api_token=ibm_token, hub='your hub', group='your group', project='your project')
+    set_ibmq_config(ibmq_api_token=ibm_token, instance=f"{hub}/{group}/{project}")
 
 Alternatively you can use the following qiskit commands to save your credentials
 locally without saving the token in pytket config:
 
 ::
 
-    from qiskit import IBMQ
+    from qiskit_ibm_provider import IBMProvider
     from qiskit_ibm_runtime import QiskitRuntimeService
 
-    IBMQ.save_account(token=ibm_token)
+    IBMProvider.save_account(token=ibm_token)
     QiskitRuntimeService.save_account(channel="ibm_quantum", token=ibm_token)
 
-To see which devices you can access you can use the ``available_devices`` method on the :py:class:`IBMQBackend` or :py:class:`IBMQEmulatorBackend`. Note that it is possible to pass ``hub``, ``group`` and ``project`` parameters to this method. This allows you to see which devices are accessible through your IBM hub.
+To see which devices you can access you can use the ``available_devices`` method on the :py:class:`IBMQBackend` or :py:class:`IBMQEmulatorBackend`. Note that it is possible to pass optional ``instance`` and ``provider`` arguments to this method. This allows you to see which devices are accessible through your IBM hub.
 
 ::
 
     from pytket.extensions.qiskit import IBMQBackend
+    from qiskit_ibm_provider import IBMProvider
 
-    backend = IBMQBackend # Initialise backend for an IBM device
-    backend.available_devices(hub='your hub', group='your group', project='your project') 
+    ibm_provider = IBMProvider()
+    backend = IBMQBackend("ibmq_belem") # Initialise backend for an IBM device
+    backend.available_devices(instance=instance=f"{hub}/{group}/{project}", provider=ibm_provider) 
 
 
 Backends Available Through pytket-qiskit
diff --git a/pytket/extensions/qiskit/__init__.py b/pytket/extensions/qiskit/__init__.py
index b95e62ab..30ab234b 100644
--- a/pytket/extensions/qiskit/__init__.py
+++ b/pytket/extensions/qiskit/__init__.py
@@ -17,7 +17,7 @@
 from ._metadata import __extension_version__, __extension_name__  # type: ignore
 from .backends import (
     IBMQBackend,
-    NoIBMQAccountError,
+    NoIBMQCredentialsError,
     AerBackend,
     AerStateBackend,
     AerUnitaryBackend,
diff --git a/pytket/extensions/qiskit/backends/__init__.py b/pytket/extensions/qiskit/backends/__init__.py
index 05a60635..1e04cde2 100644
--- a/pytket/extensions/qiskit/backends/__init__.py
+++ b/pytket/extensions/qiskit/backends/__init__.py
@@ -13,6 +13,6 @@
 # limitations under the License.
 """Backends for connecting to IBM devices and simulators directly from pytket"""
 
-from .ibm import IBMQBackend, NoIBMQAccountError
+from .ibm import IBMQBackend, NoIBMQCredentialsError
 from .aer import AerBackend, AerStateBackend, AerUnitaryBackend
 from .ibmq_emulator import IBMQEmulatorBackend
diff --git a/pytket/extensions/qiskit/backends/config.py b/pytket/extensions/qiskit/backends/config.py
index eb204674..f52de41d 100644
--- a/pytket/extensions/qiskit/backends/config.py
+++ b/pytket/extensions/qiskit/backends/config.py
@@ -23,9 +23,7 @@ class QiskitConfig(PytketExtConfig):
 
     ext_dict_key: ClassVar[str] = "qiskit"
 
-    hub: Optional[str]
-    group: Optional[str]
-    project: Optional[str]
+    instance: Optional[str]
     ibmq_api_token: Optional[str]
 
     @classmethod
@@ -33,29 +31,21 @@ def from_extension_dict(
         cls: Type["QiskitConfig"], ext_dict: Dict[str, Any]
     ) -> "QiskitConfig":
         return cls(
-            ext_dict.get("hub", None),
-            ext_dict.get("group", None),
-            ext_dict.get("project", None),
+            ext_dict.get("instance", None),
             ext_dict.get("ibmq_api_token", None),
         )
 
 
 def set_ibmq_config(
-    hub: Optional[str] = None,
-    group: Optional[str] = None,
-    project: Optional[str] = None,
+    instance: Optional[str] = None,
     ibmq_api_token: Optional[str] = None,
 ) -> None:
     """Set default values for any of hub, group, project or API token
     for your IBMQ provider. Can be overridden in backend construction."""
 
     config = QiskitConfig.from_default_config_file()
-    if hub is not None:
-        config.hub = hub
-    if group is not None:
-        config.group = group
-    if project is not None:
-        config.project = project
+    if instance is not None:
+        config.instance = instance
     if ibmq_api_token is not None:
         config.ibmq_api_token = ibmq_api_token
     config.update_default_config_file()
diff --git a/pytket/extensions/qiskit/backends/ibm.py b/pytket/extensions/qiskit/backends/ibm.py
index ba1ec0b4..4dcb0d49 100644
--- a/pytket/extensions/qiskit/backends/ibm.py
+++ b/pytket/extensions/qiskit/backends/ibm.py
@@ -12,6 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import os
 import itertools
 import logging
 from ast import literal_eval
@@ -23,16 +24,16 @@
     List,
     Optional,
     Dict,
-    Any,
     Sequence,
     TYPE_CHECKING,
     Tuple,
     Union,
+    Any,
 )
 from warnings import warn
 
-import qiskit  # type: ignore
-from qiskit import IBMQ
+from qiskit_ibm_provider import IBMProvider  # type: ignore
+from qiskit_ibm_provider.exceptions import IBMProviderError  # type: ignore
 from qiskit.primitives import SamplerResult  # type: ignore
 
 
@@ -92,10 +93,7 @@
 from .config import QiskitConfig
 
 if TYPE_CHECKING:
-    from qiskit.providers.ibmq import (  # type: ignore
-        IBMQBackend as _QiskIBMQBackend,
-        AccountProvider,
-    )
+    from qiskit_ibm_provider.ibm_backend import IBMBackend as _QiskIBMBackend  # type: ignore
 
 _DEBUG_HANDLE_PREFIX = "_MACHINE_DEBUG_"
 
@@ -110,7 +108,7 @@ def _gen_debug_results(n_qubits: int, shots: int, index: int) -> SamplerResult:
     )
 
 
-class NoIBMQAccountError(Exception):
+class NoIBMQCredentialsError(Exception):
     """Raised when there is no IBMQ account available for the backend"""
 
     def __init__(self) -> None:
@@ -124,14 +122,16 @@ def _save_ibmq_auth(qiskit_config: Optional[QiskitConfig]) -> None:
     token = None
     if qiskit_config is not None:
         token = qiskit_config.ibmq_api_token
-    if not IBMQ.active_account():
-        if IBMQ.stored_account():
-            IBMQ.load_account()
+    if token is None and os.getenv("PYTKET_REMOTE_QISKIT_TOKEN") is not None:
+        token = os.getenv("PYTKET_REMOTE_QISKIT_TOKEN")
+    try:
+        IBMProvider()
+    except:
+        if token is not None:
+            IBMProvider.save_account(token, overwrite=True)
+            IBMProvider()
         else:
-            if token is not None:
-                IBMQ.save_account(token)
-            else:
-                raise NoIBMQAccountError()
+            raise NoIBMQCredentialsError()
     if not QiskitRuntimeService.saved_accounts():
         if token is not None:
             QiskitRuntimeService.save_account(channel="ibm_quantum", token=token)
@@ -146,11 +146,9 @@ class IBMQBackend(Backend):
     def __init__(
         self,
         backend_name: str,
-        hub: Optional[str] = None,
-        group: Optional[str] = None,
-        project: Optional[str] = None,
+        instance: Optional[str] = None,
         monitor: bool = True,
-        account_provider: Optional["AccountProvider"] = None,
+        provider: Optional["IBMProvider"] = None,
         token: Optional[str] = None,
     ):
         """A backend for running circuits on remote IBMQ devices.
@@ -158,36 +156,31 @@ def __init__(
         be specified here as parameters or set in the config file
         using :py:meth:`pytket.extensions.qiskit.set_ibmq_config`.
         This function can also be used to set the IBMQ API token.
-
-        :param backend_name: Name of the IBMQ device, e.g. `ibmqx4`,
-         `ibmq_16_melbourne`.
+        :param backend_name: Name of the IBMQ device, e.g. `ibmq_16_melbourne`.
         :type backend_name: str
         :param hub: Name of the IBMQ hub to use for the provider.
          If None, just uses the first hub found. Defaults to None.
-        :type hub: Optional[str], optional
-        :param group: Name of the IBMQ group to use for the provider. Defaults to None.
-        :type group: Optional[str], optional
-        :param project: Name of the IBMQ project to use for the provider.
-         Defaults to None.
-        :type project: Optional[str], optional
+        :param instance: A string containing information about the hub/group/project in
+          the following format. Use instance=f"{hub}/{group}/{project}".
+        :type instance: str, optional
         :param monitor: Use the IBM job monitor. Defaults to True.
         :type monitor: bool, optional
         :raises ValueError: If no IBMQ account is loaded and none exists on the disk.
-        :param account_provider: An AccountProvider returned from IBMQ.enable_account.
-         Used to pass credentials in if not configured on local machine (as well as hub,
-         group and project). Defaults to None.
-        :type account_provider: Optional[AccountProvider]
+        :param provider: An IBMProvider
+        :type provider: Optional[IBMProvider]
         :param token: Authentication token to use the `QiskitRuntimeService`.
         :type token: Optional[str]
         """
         super().__init__()
-        self._pytket_config = QiskitConfig.from_default_config_file()
+        self._pytket_config = (
+            QiskitConfig.from_default_config_file()
+        )  # it looks like this is not working?
         self._provider = (
-            self._get_provider(hub, group, project, self._pytket_config)
-            if account_provider is None
-            else account_provider
+            self._get_provider(instance=instance, qiskit_config=self._pytket_config)
+            if provider is None
+            else provider
         )
-        self._backend: "_QiskIBMQBackend" = self._provider.get_backend(backend_name)
+        self._backend: "_QiskIBMBackend" = self._provider.get_backend(backend_name)  # type: ignore
         config = self._backend.configuration()
         self._max_per_job = getattr(config, "max_experiments", 1)
 
@@ -208,33 +201,16 @@ def __init__(
 
     @staticmethod
     def _get_provider(
-        hub: Optional[str],
-        group: Optional[str],
-        project: Optional[str],
+        instance: Optional[str],
         qiskit_config: Optional[QiskitConfig],
-    ) -> "AccountProvider":
+    ) -> "IBMProvider":
         _save_ibmq_auth(qiskit_config)
-        provider_kwargs: Dict[str, Optional[str]] = {}
-        if hub:
-            provider_kwargs["hub"] = hub
-        else:
-            provider_kwargs["hub"] = qiskit_config.hub if qiskit_config else None
-        if group:
-            provider_kwargs["group"] = group
-        else:
-            provider_kwargs["group"] = qiskit_config.group if qiskit_config else None
-        if project:
-            provider_kwargs["project"] = project
-        else:
-            provider_kwargs["project"] = (
-                qiskit_config.project if qiskit_config else None
-            )
         try:
-            if any(x is not None for x in provider_kwargs.values()):
-                provider = IBMQ.get_provider(**provider_kwargs)
+            if instance is not None:
+                provider = IBMProvider(instance=instance)
             else:
-                provider = IBMQ.providers()[0]
-        except qiskit.providers.ibmq.exceptions.IBMQProviderError as err:
+                provider = IBMProvider()
+        except IBMProviderError as err:
             logging.warn(
                 (
                     "Provider was not specified enough, specify hub,"
@@ -250,7 +226,7 @@ def backend_info(self) -> BackendInfo:
         return self._backend_info
 
     @classmethod
-    def _get_backend_info(cls, backend: "_QiskIBMQBackend") -> BackendInfo:
+    def _get_backend_info(cls, backend: "_QiskIBMBackend") -> BackendInfo:
         config = backend.configuration()
         characterisation = process_characterisation(backend)
         averaged_errors = get_avg_characterisation(characterisation)
@@ -284,7 +260,7 @@ def _get_backend_info(cls, backend: "_QiskIBMQBackend") -> BackendInfo:
         gate_set = _tk_gate_set(backend)
         backend_info = BackendInfo(
             cls.__name__,
-            backend.name(),
+            backend.name,
             __extension_version__,
             arch,
             gate_set.union(
@@ -310,11 +286,12 @@ def _get_backend_info(cls, backend: "_QiskIBMQBackend") -> BackendInfo:
 
     @classmethod
     def available_devices(cls, **kwargs: Any) -> List[BackendInfo]:
-        provider: Optional["AccountProvider"] = kwargs.get("account_provider")
+        provider = kwargs.get("provider")
         if provider is None:
-            provider = cls._get_provider(
-                kwargs.get("hub"), kwargs.get("group"), kwargs.get("project"), None
-            )
+            if kwargs.get("instance") is not None:
+                provider = cls._get_provider(kwargs.get("instance"), None)
+            provider = IBMProvider()
+
         backend_info_list = [
             cls._get_backend_info(backend) for backend in provider.backends()
         ]
diff --git a/pytket/extensions/qiskit/backends/ibmq_emulator.py b/pytket/extensions/qiskit/backends/ibmq_emulator.py
index 9c1c5aa6..9ad272ce 100644
--- a/pytket/extensions/qiskit/backends/ibmq_emulator.py
+++ b/pytket/extensions/qiskit/backends/ibmq_emulator.py
@@ -27,9 +27,7 @@
 )
 from warnings import warn
 
-from qiskit.providers.aer import AerSimulator  # type: ignore
 from qiskit.providers.aer.noise.noise_model import NoiseModel  # type: ignore
-from qiskit.providers.ibmq import AccountProvider  # type: ignore
 from qiskit_ibm_runtime import (  # type: ignore
     QiskitRuntimeService,
     Session,
@@ -37,6 +35,7 @@
     Sampler,
     RuntimeJob,
 )
+from qiskit_ibm_provider import IBMProvider  # type: ignore
 
 from pytket.backends import Backend, CircuitNotRunError, ResultHandle, CircuitStatus
 from pytket.backends.backendinfo import BackendInfo
@@ -70,10 +69,8 @@ class IBMQEmulatorBackend(Backend):
     def __init__(
         self,
         backend_name: str,
-        hub: Optional[str] = None,
-        group: Optional[str] = None,
-        project: Optional[str] = None,
-        account_provider: Optional["AccountProvider"] = None,
+        instance: Optional[str] = None,
+        provider: Optional["IBMProvider"] = None,
         token: Optional[str] = None,
     ):
         """Construct an IBMQEmulatorBackend. Identical to :py:class:`IBMQBackend`
@@ -83,10 +80,8 @@ def __init__(
         super().__init__()
         self._ibmq = IBMQBackend(
             backend_name=backend_name,
-            hub=hub,
-            group=group,
-            project=project,
-            account_provider=account_provider,
+            instance=instance,
+            provider=provider,
             token=token,
         )
 
@@ -94,8 +89,7 @@ def __init__(
         self._session = Session(service=self._service, backend="ibmq_qasm_simulator")
 
         # Get noise model:
-        aer_sim = AerSimulator.from_backend(self._ibmq._backend)
-        self._noise_model = NoiseModel.from_backend(aer_sim)
+        self._noise_model = NoiseModel.from_backend(self._ibmq._backend)
 
         # cache of results keyed by job id and circuit index
         self._ibm_res_cache: Dict[Tuple[str, int], Counter] = dict()
diff --git a/pytket/extensions/qiskit/tket_pass.py b/pytket/extensions/qiskit/tket_pass.py
index 3d135933..9e396e74 100644
--- a/pytket/extensions/qiskit/tket_pass.py
+++ b/pytket/extensions/qiskit/tket_pass.py
@@ -17,8 +17,10 @@
 from qiskit.providers import BackendV1  # type: ignore
 from qiskit.transpiler.basepasses import TransformationPass, BasePass as qBasePass  # type: ignore
 from qiskit.converters import circuit_to_dag, dag_to_circuit  # type: ignore
-from qiskit.providers.aer.aerprovider import AerProvider  # type: ignore
-from qiskit.providers.ibmq.accountprovider import AccountProvider  # type: ignore
+from qiskit.providers.aer.aerprovider import AerProvider  # type: ignore # type: ignore
+from qiskit_ibm_provider import IBMProvider  # type: ignore
+
+
 from pytket.passes import BasePass  # type: ignore
 from pytket.extensions.qiskit import (
     IBMQBackend,
@@ -95,8 +97,8 @@ def __init__(
         """
         if isinstance(backend._provider, AerProvider):
             tk_backend = self._aer_backend_map[backend.name()]()
-        elif isinstance(backend._provider, AccountProvider):
-            tk_backend = IBMQBackend(backend.name(), token=token)
+        elif isinstance(backend._provider, IBMProvider):
+            tk_backend = IBMQBackend(backend.name, token=token)
         else:
             raise NotImplementedError("This backend provider is not supported.")
         super().__init__(tk_backend.default_compilation_pass(optimisation_level))
diff --git a/setup.py b/setup.py
index da2cdfb0..4d27d0a6 100644
--- a/setup.py
+++ b/setup.py
@@ -48,6 +48,7 @@
         "qiskit ~= 0.42.1",
         "qiskit-ibm-runtime ~= 0.9.2",
         "qiskit-aer ~= 0.12.0",
+        "qiskit-ibm-provider ~= 0.5.0",
         "numpy",
     ],
     classifiers=[
diff --git a/tests/backend_test.py b/tests/backend_test.py
index 84a44243..911fbce2 100644
--- a/tests/backend_test.py
+++ b/tests/backend_test.py
@@ -22,13 +22,14 @@
 
 import pytest
 
-from qiskit import IBMQ  # type: ignore
-from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister
+from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister  # type: ignore
 from qiskit.circuit import Parameter  # type: ignore
 from qiskit.providers.aer.noise.noise_model import NoiseModel  # type: ignore
 from qiskit.providers.aer.noise import ReadoutError  # type: ignore
 from qiskit.providers.aer.noise.errors import depolarizing_error, pauli_error  # type: ignore
 
+from qiskit_ibm_provider import IBMProvider  # type: ignore
+
 from pytket.circuit import Circuit, OpType, BasisOrder, Qubit, reg_eq, Unitary2qBox  # type: ignore
 from pytket.passes import CliffordSimp  # type: ignore
 from pytket.pauli import Pauli, QubitPauliString  # type: ignore
@@ -51,7 +52,10 @@
     AerUnitaryBackend,
     IBMQEmulatorBackend,
 )
-from pytket.extensions.qiskit import qiskit_to_tk, process_characterisation
+from pytket.extensions.qiskit import (
+    qiskit_to_tk,
+    process_characterisation,
+)
 from pytket.utils.expectations import (
     get_pauli_expectation_value,
     get_operator_expectation_value,
@@ -150,14 +154,9 @@ def test_measures() -> None:
 
 
 @pytest.mark.skipif(skip_remote_tests, reason=REASON)
-def test_noise() -> None:
-    if not IBMQ.active_account():
-        IBMQ.load_account()
+def test_noise(manila_backend: IBMQBackend) -> None:
 
-    provider = IBMQ.providers(hub="ibm-q", group="open")[0]
-    back = provider.get_backend("ibmq_manila")
-
-    noise_model = NoiseModel.from_backend(back)
+    noise_model = NoiseModel.from_backend(manila_backend._backend)
     n_qbs = 5
     c = Circuit(n_qbs, n_qbs)
     x_qbs = [2, 0, 4]
@@ -196,15 +195,11 @@ def test_noise() -> None:
     assert shots.shape == (10, 4)
 
 
+@pytest.mark.timeout(None)
 @pytest.mark.skipif(skip_remote_tests, reason=REASON)
-def test_process_characterisation() -> None:
-    if not IBMQ.active_account():
-        IBMQ.load_account()
-
-    provider = IBMQ.providers(hub="ibm-q", group="open")[0]
-    back = provider.get_backend("ibmq_manila")
+def test_process_characterisation(manila_backend: IBMQBackend) -> None:
 
-    char = process_characterisation(back)
+    char = process_characterisation(manila_backend._backend)
     arch: Architecture = char.get("Architecture", Architecture([]))
     node_errors: dict = char.get("NodeErrors", {})
     link_errors: dict = char.get("EdgeErrors", {})
@@ -502,14 +497,9 @@ def test_default_pass(manila_backend: IBMQBackend) -> None:
 
 
 @pytest.mark.skipif(skip_remote_tests, reason=REASON)
-def test_aer_default_pass() -> None:
-    if not IBMQ.active_account():
-        IBMQ.load_account()
+def test_aer_default_pass(manila_backend: IBMQBackend) -> None:
 
-    provider = IBMQ.providers(hub="ibm-q", group="open")[0]
-    back = provider.get_backend("ibmq_manila")
-
-    noise_model = NoiseModel.from_backend(back)
+    noise_model = NoiseModel.from_backend(manila_backend._backend)
     for nm in [None, noise_model]:
         b = AerBackend(nm)
         for ol in range(3):
@@ -772,7 +762,7 @@ def test_mixed_circuit() -> None:
 
 
 @pytest.mark.skipif(skip_remote_tests, reason=REASON)
-def test_aer_placed_expectation() -> None:
+def test_aer_placed_expectation(manila_backend: IBMQBackend) -> None:
     # bug TKET-695
     n_qbs = 3
     c = Circuit(n_qbs, n_qbs)
@@ -790,13 +780,7 @@ def test_aer_placed_expectation() -> None:
     )
     assert b.get_operator_expectation_value(c, operator) == (-0.5 + 0j)
 
-    if not IBMQ.active_account():
-        IBMQ.load_account()
-
-    provider = IBMQ.providers(hub="ibm-q", group="open")[0]
-    back = provider.get_backend("ibmq_manila")
-
-    noise_model = NoiseModel.from_backend(back)
+    noise_model = NoiseModel.from_backend(manila_backend._backend)
 
     noise_b = AerBackend(noise_model)
 
@@ -1132,15 +1116,11 @@ def test_cloud_stabiliser(simulator_stabilizer_backend: IBMQBackend) -> None:
 
 
 @pytest.mark.skipif(skip_remote_tests, reason=REASON)
-def test_available_devices() -> None:
-    backend_info_list = IBMQBackend.available_devices(
-        hub="ibm-q", group="open", project="main"
-    )
+def test_available_devices(ibm_provider: IBMProvider) -> None:
+    backend_info_list = IBMQBackend.available_devices(instance="ibm-q/open/main")
     assert len(backend_info_list) > 0
 
-    provider = IBMQ.providers(hub="ibm-q", group="open")[0]
-
-    backend_info_list = IBMQBackend.available_devices(account_provider=provider)
+    backend_info_list = IBMQBackend.available_devices(provider=ibm_provider)
     assert len(backend_info_list) > 0
 
     backend_info_list = IBMQBackend.available_devices()
diff --git a/tests/conftest.py b/tests/conftest.py
index 3a34a34e..6aabcb07 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -14,7 +14,8 @@
 
 import os
 import pytest
-from qiskit import IBMQ  # type: ignore
+
+from qiskit_ibm_provider import IBMProvider  # type: ignore
 from pytket.extensions.qiskit import IBMQBackend, IBMQEmulatorBackend
 
 
@@ -26,19 +27,20 @@ def setup_qiskit_account() -> None:
         # to enable one using the token in the env variable:
         # PYTKET_REMOTE_QISKIT_TOKEN
         # Note: The IBMQ account will only be enabled for the current session
-        if not IBMQ.stored_account():
+        try:
+            IBMProvider()
+        except:
             token = os.getenv("PYTKET_REMOTE_QISKIT_TOKEN")
             if token:
-                IBMQ.enable_account(token)
+                IBMProvider.save_account(token, overwrite=True)
+                IBMProvider()
 
 
 @pytest.fixture(scope="module")
 def manila_backend() -> IBMQBackend:
     return IBMQBackend(
         "ibmq_manila",
-        hub="ibm-q",
-        group="open",
-        project="main",
+        instance="ibm-q/open/main",
         token=os.getenv("PYTKET_REMOTE_QISKIT_TOKEN"),
     )
 
@@ -47,9 +49,7 @@ def manila_backend() -> IBMQBackend:
 def lima_backend() -> IBMQBackend:
     return IBMQBackend(
         "ibmq_lima",
-        hub="ibm-q",
-        group="open",
-        project="main",
+        instance="ibm-q/open/main",
         token=os.getenv("PYTKET_REMOTE_QISKIT_TOKEN"),
     )
 
@@ -58,9 +58,7 @@ def lima_backend() -> IBMQBackend:
 def qasm_simulator_backend() -> IBMQBackend:
     return IBMQBackend(
         "ibmq_qasm_simulator",
-        hub="ibm-q",
-        group="open",
-        project="main",
+        instance="ibm-q/open/main",
         token=os.getenv("PYTKET_REMOTE_QISKIT_TOKEN"),
     )
 
@@ -69,9 +67,7 @@ def qasm_simulator_backend() -> IBMQBackend:
 def simulator_stabilizer_backend() -> IBMQBackend:
     return IBMQBackend(
         "simulator_stabilizer",
-        hub="ibm-q",
-        group="open",
-        project="main",
+        instance="ibm-q/open/main",
         monitor=False,
         token=os.getenv("PYTKET_REMOTE_QISKIT_TOKEN"),
     )
@@ -81,9 +77,7 @@ def simulator_stabilizer_backend() -> IBMQBackend:
 def manila_emulator_backend() -> IBMQEmulatorBackend:
     return IBMQEmulatorBackend(
         "ibmq_manila",
-        hub="ibm-q",
-        group="open",
-        project="main",
+        instance="ibm-q/open/main",
         token=os.getenv("PYTKET_REMOTE_QISKIT_TOKEN"),
     )
 
@@ -92,8 +86,17 @@ def manila_emulator_backend() -> IBMQEmulatorBackend:
 def belem_emulator_backend() -> IBMQEmulatorBackend:
     return IBMQEmulatorBackend(
         "ibmq_belem",
-        hub="ibm-q",
-        group="open",
-        project="main",
+        instance="ibm-q/open/main",
         token=os.getenv("PYTKET_REMOTE_QISKIT_TOKEN"),
     )
+
+
+@pytest.fixture(scope="module")
+def ibm_provider() -> IBMProvider:
+    token = os.getenv("PYTKET_REMOTE_QISKIT_TOKEN")
+
+    try:
+        return IBMProvider(instance="ibm-q/open/main")
+    except:
+        token = os.getenv("PYTKET_REMOTE_QISKIT_TOKEN")
+        return IBMProvider(token=token, instance="ibm-q/open/main", overwrite=True)
diff --git a/tests/qiskit_backend_test.py b/tests/qiskit_backend_test.py
index 891f81fe..939bec98 100644
--- a/tests/qiskit_backend_test.py
+++ b/tests/qiskit_backend_test.py
@@ -18,8 +18,7 @@
 import numpy as np
 import pytest
 
-from qiskit import IBMQ, QuantumCircuit, execute  # type: ignore
-from qiskit.providers.ibmq import AccountProvider  # type: ignore
+from qiskit import QuantumCircuit, execute  # type: ignore
 from qiskit.opflow import CircuitStateFn, CircuitSampler  # type: ignore
 from qiskit.providers import JobStatus  # type: ignore
 from qiskit.utils import QuantumInstance  # type: ignore
@@ -28,6 +27,8 @@
 from qiskit.transpiler.passes import Unroller  # type: ignore
 from qiskit_aer import Aer  # type: ignore
 
+from qiskit_ibm_provider import IBMProvider  # type: ignore
+
 from pytket.extensions.qiskit import (
     AerBackend,
     AerStateBackend,
@@ -42,17 +43,15 @@
 
 skip_remote_tests: bool = os.getenv("PYTKET_RUN_REMOTE_TESTS") is None
 
-REASON = "PYTKET_RUN_REMOTE_TESTS not set (requires IBMQ configuration)"
+REASON = "PYTKET_RUN_REMOTE_TESTS not set (requires IBM configuration)"
 
 
 @pytest.fixture
-def provider() -> Optional["AccountProvider"]:
+def provider() -> Optional["IBMProvider"]:
     if skip_remote_tests:
         return None
     else:
-        if not IBMQ.active_account():
-            IBMQ.load_account()
-        return IBMQ.providers(hub="ibm-q")[0]
+        return IBMProvider(instance="ibm-q")
 
 
 def circuit_gen(measure: bool = False) -> QuantumCircuit:
diff --git a/tests/qiskit_convert_test.py b/tests/qiskit_convert_test.py
index 58a2e0eb..f2de11f1 100644
--- a/tests/qiskit_convert_test.py
+++ b/tests/qiskit_convert_test.py
@@ -23,7 +23,6 @@
     QuantumRegister,
     ClassicalRegister,
     execute,
-    IBMQ,
 )
 from qiskit.opflow import PauliOp, PauliSumOp, PauliTrotterEvolution, Suzuki  # type: ignore
 from qiskit.opflow.primitive_ops import PauliSumOp  # type: ignore
@@ -33,6 +32,7 @@
 import qiskit.circuit.library.standard_gates as qiskit_gates  # type: ignore
 from qiskit.circuit import Parameter  # type: ignore
 from qiskit_aer import Aer  # type: ignore
+
 from pytket.circuit import (  # type: ignore
     Circuit,
     CircBox,
@@ -43,7 +43,7 @@
     CustomGateDef,
     reg_eq,
 )
-from pytket.extensions.qiskit import tk_to_qiskit, qiskit_to_tk
+from pytket.extensions.qiskit import tk_to_qiskit, qiskit_to_tk, IBMQBackend
 from pytket.extensions.qiskit.qiskit_convert import _gate_str_2_optype
 from pytket.extensions.qiskit.tket_pass import TketPass, TketAutoPass
 from pytket.extensions.qiskit.result_convert import qiskit_result_to_backendresult
@@ -56,6 +56,8 @@
 
 skip_remote_tests: bool = os.getenv("PYTKET_RUN_REMOTE_TESTS") is None
 
+REASON = "PYTKET_RUN_REMOTE_TESTS not set (requires IBM configuration)"
+
 
 def test_classical_barrier_error() -> None:
     c = Circuit(1, 1)
@@ -267,17 +269,15 @@ def test_tketpass() -> None:
     assert np.allclose(u1, u2)
 
 
-def test_tketautopass() -> None:
+@pytest.mark.timeout(None)
+@pytest.mark.skipif(skip_remote_tests, reason=REASON)
+def test_tketautopass(manila_backend: IBMQBackend) -> None:
     backends = [
         Aer.get_backend("aer_simulator_statevector"),
         Aer.get_backend("aer_simulator"),
         Aer.get_backend("aer_simulator_unitary"),
     ]
-    if not skip_remote_tests:
-        if not IBMQ.active_account():
-            IBMQ.load_account()
-        provider = IBMQ.providers(hub="ibm-q")[0]
-        backends.append(provider.get_backend("ibmq_manila"))
+    backends.append(manila_backend._backend)
     for back in backends:
         for o_level in range(3):
             tkpass = TketAutoPass(

From 3d966e2d305355507c9397c40a060b5c26806827 Mon Sep 17 00:00:00 2001
From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com>
Date: Fri, 16 Jun 2023 15:08:48 +0100
Subject: [PATCH 04/10] handle state prep boxes in qiskit converters (#109)

* handle state prep boxes in qiskit_to_tk

* try to fix tk_to_qiskit direction

* fix tk_to_qiskit and format code

* update test

* remove unecessary elif statement

* black

* check that Initialize is constructed with a list

* add missing typecheck

* use list in test

* fix bad syntax in list

* format with black

* update changelog

* fix mypy error with list vs array

* add logic for strings and ints

* comment

* add new handling of strings

* fix handling of strings using resets

* small change

* update logic to handle qiskit's typing

* add addtional test for state prep with int and str

* simplify string parsing and add resets

* update tests

* add StatePreparation handling

* simplify case handling

* fix stateprepbox reset

* test statevector handling

* handle resets in StatePreparationBox

* add addtional test for resets

* linting

* shorten comment

* don't modify instr.params[0]

* correct endianness

* improve tests

* comment and remove import

* black

* remove unused import

* fix typing

* add some comments

* use reversed

* make mypy happy

* improve changelog

* black format after resolving merge conflicts

* make suggested changes to _string_to_circuit helper function

* Update pytket/extensions/qiskit/qiskit_convert.py

Co-authored-by: Alec Edgington <54802828+cqc-alec@users.noreply.github.com>

---------

Co-authored-by: Alec Edgington <54802828+cqc-alec@users.noreply.github.com>
---
 docs/changelog.rst                         |   1 +
 pytket/extensions/qiskit/qiskit_convert.py |  90 +++++++++++++++++-
 tests/qiskit_convert_test.py               | 102 +++++++++++++++++++++
 3 files changed, 192 insertions(+), 1 deletion(-)

diff --git a/docs/changelog.rst b/docs/changelog.rst
index 067c9e6b..25cc268f 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -6,6 +6,7 @@ Changelog
 
 * IBM devices are now accessed using the `qiskit-ibm-provider <https://github.com/Qiskit/qiskit-ibm-provider>`_ instead of the deprecated :py:class:`IBMQ`. This allows the newest IBM devices and simulators to be accessed through the pytket-qiskit extension.
 * Fix to the `tk_to_qiskit` converter to prevent cancellation of redundant gates when converting to qiskit.
+* Handle qiskit circuits with :py:class:`Initialize` and :py:class:`StatePreparation` instructions in the :py:meth:`qiskit_to_tk` converter. The :py:meth:`tk_to_qiskit` converter now handles :py:class:`StatePreparationBox`.
 * Fix handling of control state in `qiskit_to_tk`.
 
 0.39.0 (May 2023)
diff --git a/pytket/extensions/qiskit/qiskit_convert.py b/pytket/extensions/qiskit/qiskit_convert.py
index 3dc32614..0be2ee1e 100644
--- a/pytket/extensions/qiskit/qiskit_convert.py
+++ b/pytket/extensions/qiskit/qiskit_convert.py
@@ -53,9 +53,10 @@
     ParameterExpression,
     Reset,
 )
-from qiskit.circuit.library import CRYGate, RYGate, PauliEvolutionGate  # type: ignore
+from qiskit.circuit.library import CRYGate, RYGate, PauliEvolutionGate, StatePreparation  # type: ignore
 
 from qiskit.extensions.unitary import UnitaryGate  # type: ignore
+from qiskit.extensions import Initialize  # type: ignore
 from pytket.circuit import (  # type: ignore
     CircBox,
     Circuit,
@@ -68,6 +69,7 @@
     Bit,
     Qubit,
     QControlBox,
+    StatePreparationBox,
 )
 from pytket._tket.circuit import _TEMP_BIT_NAME  # type: ignore
 from pytket.pauli import Pauli, QubitPauliString  # type: ignore
@@ -148,6 +150,8 @@
     Measure: OpType.Measure,
     Reset: OpType.Reset,
     UnitaryGate: OpType.Unitary2qBox,
+    Initialize: OpType.StatePreparationBox,
+    StatePreparation: OpType.StatePreparationBox,
 }
 
 _known_qiskit_gate = {**_qiskit_gates_1q, **_qiskit_gates_2q, **_qiskit_gates_other}
@@ -242,6 +246,46 @@ def _qpo_from_peg(peg: PauliEvolutionGate, qubits: List[Qubit]) -> QubitPauliOpe
     return QubitPauliOperator(qpodict)
 
 
+def _string_to_circuit(
+    circuit_string: str, n_qubits: int, qiskit_instruction: Instruction
+) -> Circuit:
+    """Helper function to handle strings in QuantumCircuit.initialize
+    and QuantumCircuit.prepare_state"""
+
+    circ = Circuit(n_qubits)
+    # Check if Instruction is Initialize or Statepreparation
+    # If Initialize, add resets
+    if isinstance(qiskit_instruction, Initialize):
+        for qubit in circ.qubits:
+            circ.add_gate(OpType.Reset, [qubit])
+
+    # We iterate through the string in reverse to add the
+    # gates in the correct order (endian-ness).
+    for count, char in enumerate(reversed(circuit_string)):
+        if char == "0":
+            pass
+        elif char == "1":
+            circ.X(count)
+        elif char == "+":
+            circ.H(count)
+        elif char == "-":
+            circ.X(count)
+            circ.H(count)
+        elif char == "r":
+            circ.H(count)
+            circ.S(count)
+        elif char == "l":
+            circ.H(count)
+            circ.Sdg(count)
+        else:
+            raise ValueError(
+                f"Cannot parse string for character {char}. "
+                + "The supported characters are {'0', '1', '+', '-', 'r', 'l'}."
+            )
+
+    return circ
+
+
 class CircuitBuilder:
     def __init__(
         self,
@@ -349,6 +393,39 @@ def add_qiskit_data(self, data: "QuantumCircuitData") -> None:
                 q_ctrl_box = QControlBox(c_box, instr.num_ctrl_qubits)
                 self.tkc.add_qcontrolbox(q_ctrl_box, qubits)
 
+            elif isinstance(instr, (Initialize, StatePreparation)):
+                # Check how Initialize or StatePrep is constructed
+                if isinstance(instr.params[0], str):
+                    # Parse string to get the right single qubit gates
+                    circuit_string = "".join(instr.params)
+                    circuit = _string_to_circuit(
+                        circuit_string, instr.num_qubits, qiskit_instruction=instr
+                    )
+                    self.tkc.add_circuit(circuit, qubits)
+
+                elif isinstance(instr.params, list) and len(instr.params) != 1:
+                    amplitude_list = instr.params
+                    if isinstance(instr, Initialize):
+                        pytket_state_prep_box = StatePreparationBox(
+                            amplitude_list, with_initial_reset=True
+                        )
+                    else:
+                        pytket_state_prep_box = StatePreparationBox(
+                            amplitude_list, with_initial_reset=False
+                        )
+                    # Need to reverse qubits here (endian-ness)
+                    reversed_qubits = list(reversed(qubits))
+                    self.tkc.add_gate(pytket_state_prep_box, reversed_qubits)
+
+                elif isinstance(instr.params[0], complex) and len(instr.params) == 1:
+                    # convert int to a binary string and apply X for |1>
+                    integer_parameter = int(instr.params[0].real)
+                    bit_string = bin(integer_parameter)[2:]
+                    circuit = _string_to_circuit(
+                        bit_string, instr.num_qubits, qiskit_instruction=instr
+                    )
+                    self.tkc.add_circuit(circuit, qubits)
+
             elif type(instr) == PauliEvolutionGate:
                 qpo = _qpo_from_peg(instr, qubits)
                 empty_circ = Circuit(len(qargs))
@@ -502,6 +579,17 @@ def append_tk_command_to_qiskit(
         # Note reversal of qubits, to account for endianness (pytket unitaries are
         # ILO-BE == DLO-LE; qiskit unitaries are ILO-LE == DLO-BE).
         return qcirc.append(g, qargs=list(reversed(qargs)))
+    if optype == OpType.StatePreparationBox:
+        qargs = [qregmap[q.reg_name][q.index[0]] for q in args]
+        statevector_array = op.get_statevector()
+        # check if the StatePreparationBox contains resets
+        if op.with_initial_reset():
+            initializer = Initialize(statevector_array)
+            return qcirc.append(initializer, qargs=list(reversed(qargs)))
+        else:
+            qiskit_state_prep_box = StatePreparation(statevector_array)
+            return qcirc.append(qiskit_state_prep_box, qargs=list(reversed(qargs)))
+
     if optype == OpType.Barrier:
         if any(q.type == UnitType.bit for q in args):
             raise NotImplementedError(
diff --git a/tests/qiskit_convert_test.py b/tests/qiskit_convert_test.py
index f2de11f1..387cb131 100644
--- a/tests/qiskit_convert_test.py
+++ b/tests/qiskit_convert_test.py
@@ -32,6 +32,7 @@
 import qiskit.circuit.library.standard_gates as qiskit_gates  # type: ignore
 from qiskit.circuit import Parameter  # type: ignore
 from qiskit_aer import Aer  # type: ignore
+from qiskit.quantum_info import Statevector
 
 from pytket.circuit import (  # type: ignore
     Circuit,
@@ -42,6 +43,7 @@
     Bit,
     CustomGateDef,
     reg_eq,
+    StatePreparationBox,
 )
 from pytket.extensions.qiskit import tk_to_qiskit, qiskit_to_tk, IBMQBackend
 from pytket.extensions.qiskit.qiskit_convert import _gate_str_2_optype
@@ -58,6 +60,15 @@
 
 REASON = "PYTKET_RUN_REMOTE_TESTS not set (requires IBM configuration)"
 
+# helper function for testing
+def _get_qiskit_statevector(qc: QuantumCircuit) -> np.ndarray:
+    """Given a QuantumCircuit, use aer_simulator_statevector to compute its
+    statevector, return the vector with its endianness adjusted"""
+    back = Aer.get_backend("aer_simulator_statevector")
+    qc.save_state()
+    job = back.run(qc)
+    return np.array(job.result().data()["statevector"].reverse_qargs().data)
+
 
 def test_classical_barrier_error() -> None:
     c = Circuit(1, 1)
@@ -795,3 +806,94 @@ def test_ccx_conversion() -> None:
         qiskit_to_tk(c11).get_unitary(),
         Circuit(3).CCX(0, 1, 2).get_unitary(),
     )
+
+
+# https://github.com/CQCL/pytket-qiskit/issues/100
+def test_state_prep_conversion_array_or_list() -> None:
+    # State prep with list of real amplitudes
+    ghz_state_permuted = np.array([0, 0, 1 / np.sqrt(2), 0, 0, 0, 0, 1 / np.sqrt(2)])
+    qc_sp = QuantumCircuit(3)
+    qc_sp.prepare_state(ghz_state_permuted)
+    tk_sp = qiskit_to_tk(qc_sp)
+    assert tk_sp.n_gates_of_type(OpType.StatePreparationBox) == 1
+    assert tk_sp.n_gates == 1
+    assert compare_statevectors(tk_sp.get_statevector(), ghz_state_permuted)
+    # State prep with ndarray of complex amplitudes
+    qc_sp2 = QuantumCircuit(2)
+    complex_statvector = np.array([1 / np.sqrt(2), 0, -1.0j / np.sqrt(2), 0])
+    qc_sp2.initialize(complex_statvector, qc_sp2.qubits)
+    tk_sp2 = qiskit_to_tk(qc_sp2)
+    assert tk_sp2.n_gates_of_type(OpType.StatePreparationBox) == 1
+    assert tk_sp2.n_gates == 1
+    # test tket -> qiskit conversion
+    converted_qiskit_qc = tk_to_qiskit(tk_sp2)
+    assert converted_qiskit_qc.count_ops()["initialize"] == 1
+    tk_sp3 = qiskit_to_tk(converted_qiskit_qc)
+    # check circuit decomposes as expected
+    DecomposeBoxes().apply(tk_sp3)
+    assert tk_sp3.n_gates_of_type(OpType.Reset) == 2
+    state_arr = 1 / np.sqrt(2) * np.array([1, 1, 0, 0])
+    sv = Statevector(state_arr)
+    qc_2 = QuantumCircuit(2)
+    qc_2.prepare_state(sv, [0, 1])
+    tkc_2 = qiskit_to_tk(qc_2)
+    assert tkc_2.n_gates_of_type(OpType.StatePreparationBox) == 1
+
+
+def test_state_prep_conversion_with_int() -> None:
+    qc = QuantumCircuit(4)
+    qc.prepare_state(7, qc.qubits)
+    tkc7 = qiskit_to_tk(qc)
+    assert tkc7.n_gates_of_type(OpType.X) == 3
+    qc_sv = _get_qiskit_statevector(qc.decompose())
+    assert compare_statevectors(tkc7.get_statevector(), qc_sv)
+    int_statevector = Statevector.from_int(5, 8)
+    qc_s = QuantumCircuit(3)
+    qc_s.prepare_state(int_statevector)
+    # unfortunately Aer doesn't support state_preparation
+    # instructions so we decompose first
+    d_qc_s = qc_s.decompose(reps=5)
+    sv_int = _get_qiskit_statevector(d_qc_s)
+    tkc_int = qiskit_to_tk(qc_s)
+    tkc_int_sv = tkc_int.get_statevector()
+    assert compare_statevectors(tkc_int_sv, sv_int)
+
+
+def test_state_prep_conversion_with_str() -> None:
+    qc = QuantumCircuit(5)
+    qc.initialize("rl+-1")
+    tk_circ = qiskit_to_tk(qc)
+    assert tk_circ.n_gates_of_type(OpType.Reset) == 5
+    assert tk_circ.n_gates_of_type(OpType.H) == 4
+    assert tk_circ.n_gates_of_type(OpType.X) == 2
+    qc_string_sp = QuantumCircuit(3)
+    qc_string_sp.prepare_state("r-l")
+    decomposed_qc = qc_string_sp.decompose(reps=4)
+    qiskit_sv = _get_qiskit_statevector(decomposed_qc)
+    tk_string_sp = qiskit_to_tk(qc_string_sp)
+    assert tk_string_sp.n_gates_of_type(OpType.H) == 3
+    assert tk_string_sp.n_gates_of_type(OpType.Sdg) == 1
+    assert compare_statevectors(qiskit_sv, tk_string_sp.get_statevector())
+    sv_str = Statevector.from_label("rr+-")
+    sv_qc = QuantumCircuit(4)
+    sv_qc.prepare_state(sv_str)
+    decomposed_sv_qc = sv_qc.decompose(reps=6)
+    sv_array = _get_qiskit_statevector(decomposed_sv_qc)
+    tkc_sv = qiskit_to_tk(sv_qc)
+    assert compare_statevectors(sv_array, tkc_sv.get_statevector())
+
+
+def test_conversion_to_tket_with_and_without_resets() -> None:
+    test_state = 1 / np.sqrt(3) * np.array([1, 1, 0, 0, 0, 0, 1, 0])
+    tket_sp_reset = StatePreparationBox(test_state, with_initial_reset=True)
+    tk_circ_reset = Circuit(3).add_gate(tket_sp_reset, [0, 1, 2])
+    qiskit_qc_init = tk_to_qiskit(tk_circ_reset)
+    assert qiskit_qc_init.count_ops()["initialize"] == 1
+    tket_sp_no_reset = StatePreparationBox(test_state, with_initial_reset=False)
+    tket_circ_no_reset = Circuit(3).add_gate(tket_sp_no_reset, [0, 1, 2])
+    tkc_sv = tket_circ_no_reset.get_statevector()
+    qiskit_qc_sp = tk_to_qiskit(tket_circ_no_reset)
+    assert qiskit_qc_sp.count_ops()["state_preparation"] == 1
+    decomp_qc = qiskit_qc_sp.decompose(reps=5)
+    qiskit_state = _get_qiskit_statevector(decomp_qc)
+    assert compare_statevectors(tkc_sv, qiskit_state)

From d85aca4838004ea3af1d10b0bd43b5159df13356 Mon Sep 17 00:00:00 2001
From: cqc-melf <70640934+cqc-melf@users.noreply.github.com>
Date: Fri, 16 Jun 2023 15:10:05 +0100
Subject: [PATCH 05/10] update qiskit to 0.43.0 (#113)

* update qiskit to 0.43.0

* Update setup.py

* Update changelog.rst

* Update docs/changelog.rst

* update qiskit, pytket and other packages

* update errormessage in testpackage

* update changelog

* add pytket update to changelog
---
 _metadata.py                 | 2 +-
 docs/changelog.rst           | 8 ++++++--
 setup.py                     | 8 ++++----
 tests/qiskit_backend_test.py | 2 +-
 4 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/_metadata.py b/_metadata.py
index da4d1a4e..6284418b 100644
--- a/_metadata.py
+++ b/_metadata.py
@@ -1,2 +1,2 @@
-__extension_version__ = "0.39.0"
+__extension_version__ = "0.40.0rc0"
 __extension_name__ = "pytket-qiskit"
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 25cc268f..3830b767 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -1,13 +1,17 @@
 Changelog
 ~~~~~~~~~
 
-0.40.0 (unreleased)
--------------------
+0.40.0rc0 (unreleased)
+----------------------
 
 * IBM devices are now accessed using the `qiskit-ibm-provider <https://github.com/Qiskit/qiskit-ibm-provider>`_ instead of the deprecated :py:class:`IBMQ`. This allows the newest IBM devices and simulators to be accessed through the pytket-qiskit extension.
 * Fix to the `tk_to_qiskit` converter to prevent cancellation of redundant gates when converting to qiskit.
 * Handle qiskit circuits with :py:class:`Initialize` and :py:class:`StatePreparation` instructions in the :py:meth:`qiskit_to_tk` converter. The :py:meth:`tk_to_qiskit` converter now handles :py:class:`StatePreparationBox`.
 * Fix handling of control state in `qiskit_to_tk`.
+* Update qiskit version to 0.43.1
+* Update qiskit-ibm-runtime version to 0.11.1
+* Update qiskit-ibm-provider version to 0.6.1
+* Update pytket version to 1.16
 
 0.39.0 (May 2023)
 -----------------
diff --git a/setup.py b/setup.py
index 4d27d0a6..4594bbad 100644
--- a/setup.py
+++ b/setup.py
@@ -44,11 +44,11 @@
     packages=find_namespace_packages(include=["pytket.*"]),
     include_package_data=True,
     install_requires=[
-        "pytket ~= 1.15",
-        "qiskit ~= 0.42.1",
-        "qiskit-ibm-runtime ~= 0.9.2",
+        "pytket ~= 1.16",
+        "qiskit ~= 0.43.1",
+        "qiskit-ibm-runtime ~= 0.11.1",
         "qiskit-aer ~= 0.12.0",
-        "qiskit-ibm-provider ~= 0.5.0",
+        "qiskit-ibm-provider ~= 0.6.1",
         "numpy",
     ],
     classifiers=[
diff --git a/tests/qiskit_backend_test.py b/tests/qiskit_backend_test.py
index 939bec98..ef6c5e6d 100644
--- a/tests/qiskit_backend_test.py
+++ b/tests/qiskit_backend_test.py
@@ -184,7 +184,7 @@ def is_good_state(bitstr: Any) -> bool:
     # (tested with qiskit 0.39.1)
     with pytest.raises(TranspilerError) as e:
         result = grover.amplify(problem)
-    err_msg = "Unable to map"
+    err_msg = "Unable to translate"
     assert err_msg in str(e.value)
 
     # By providing an Unroller pass, the QuantumInstance will rebase the Grover op into

From faf2f11d193c9065c553565e3ebada496f48bb58 Mon Sep 17 00:00:00 2001
From: cqc-melf <70640934+cqc-melf@users.noreply.github.com>
Date: Fri, 16 Jun 2023 16:02:36 +0100
Subject: [PATCH 06/10] always overwrite token of the ibm provider (#120)

---
 pytket/extensions/qiskit/backends/ibm.py | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/pytket/extensions/qiskit/backends/ibm.py b/pytket/extensions/qiskit/backends/ibm.py
index 4dcb0d49..d4035122 100644
--- a/pytket/extensions/qiskit/backends/ibm.py
+++ b/pytket/extensions/qiskit/backends/ibm.py
@@ -125,7 +125,10 @@ def _save_ibmq_auth(qiskit_config: Optional[QiskitConfig]) -> None:
     if token is None and os.getenv("PYTKET_REMOTE_QISKIT_TOKEN") is not None:
         token = os.getenv("PYTKET_REMOTE_QISKIT_TOKEN")
     try:
-        IBMProvider()
+        if token is not None:
+            IBMProvider.save_account(token, overwrite=True)
+        else:
+            IBMProvider()
     except:
         if token is not None:
             IBMProvider.save_account(token, overwrite=True)

From 1110d9e096b4bf0ae4788d844763ee89fb1bb9d1 Mon Sep 17 00:00:00 2001
From: cqc-melf <70640934+cqc-melf@users.noreply.github.com>
Date: Fri, 16 Jun 2023 16:28:35 +0100
Subject: [PATCH 07/10] update changelog and add provider check (#121)

---
 _metadata.py                             | 2 +-
 docs/changelog.rst                       | 2 +-
 pytket/extensions/qiskit/backends/ibm.py | 1 +
 3 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/_metadata.py b/_metadata.py
index 6284418b..225eaf0f 100644
--- a/_metadata.py
+++ b/_metadata.py
@@ -1,2 +1,2 @@
-__extension_version__ = "0.40.0rc0"
+__extension_version__ = "0.40.0rc1"
 __extension_name__ = "pytket-qiskit"
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 3830b767..e73a6435 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -1,7 +1,7 @@
 Changelog
 ~~~~~~~~~
 
-0.40.0rc0 (unreleased)
+0.40.0rc1 (unreleased)
 ----------------------
 
 * IBM devices are now accessed using the `qiskit-ibm-provider <https://github.com/Qiskit/qiskit-ibm-provider>`_ instead of the deprecated :py:class:`IBMQ`. This allows the newest IBM devices and simulators to be accessed through the pytket-qiskit extension.
diff --git a/pytket/extensions/qiskit/backends/ibm.py b/pytket/extensions/qiskit/backends/ibm.py
index d4035122..33ab6a83 100644
--- a/pytket/extensions/qiskit/backends/ibm.py
+++ b/pytket/extensions/qiskit/backends/ibm.py
@@ -127,6 +127,7 @@ def _save_ibmq_auth(qiskit_config: Optional[QiskitConfig]) -> None:
     try:
         if token is not None:
             IBMProvider.save_account(token, overwrite=True)
+            IBMProvider()
         else:
             IBMProvider()
     except:

From 829542219ce7a0118a2e44730805e254e3f7bb8c Mon Sep 17 00:00:00 2001
From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com>
Date: Wed, 21 Jun 2023 10:14:01 +0100
Subject: [PATCH 08/10] Support ECR gate in the gateset rebase and use
 pytest-rerunfailures (#114)

* add custom rebase to ECR

* add GateSet enum and rebase logic

* fix indent

* fix attribute name

* typo

* fix enum syntax

* black format

* add exception for when  no rebase is available

* handle ecr properly

* define gatesets as variables

* more cleanup

* minor changes

* remove duplicate variables

* remove varaible assignments

* use enum values

* remove standard gateset attribute

* redo logic and exception handling

* fix docstring typo

* changed to _get_primitive gates

* fix type annotation

* unsupported ops in expection handling

* GateSet.x -> GateSet.x.value

* black

* missing arg in _primitive_gates

* remove unecessary arg

* remove None from enum

* shorten line for pylint

* simplify handling based on feedback

* fix typos

* remove unused import

* update changelog

* 1.15 in setup.py

* fix typo in rebase

* add print statement debug

* debug default pass for stabilzer sim

* use pytest rerun failures instead of notimeout markers

* replace all notimeout markers

* fix typos in reruns

* black

* resolve rebase bug with remote stabilizer test

* update changelog and remove comment

* rerun tests with a 10s delay

* add test for compiling to ECR gateset

* add rerun marker to ecr gate compilation test

* formatting

* move assert statement
---
 docs/changelog.rst                       |  6 +--
 pytket/extensions/qiskit/backends/ibm.py | 49 +++++++++++++++++++-----
 tests/backend_test.py                    | 39 +++++++++++++++++--
 tests/conftest.py                        |  9 +++++
 tests/test-requirements.txt              |  2 +-
 5 files changed, 89 insertions(+), 16 deletions(-)

diff --git a/docs/changelog.rst b/docs/changelog.rst
index e73a6435..52bc78c0 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -1,9 +1,9 @@
 Changelog
 ~~~~~~~~~
 
-0.40.0rc1 (unreleased)
-----------------------
-
+0.40.0(unreleased)
+------------------
+* Added support for the {X, SX, Rz, ECR} in the default compilation pass for :py:class:`IBMQBackend` and :py:class:`IBMQEmulatorBackend`. This is the set of gates used by some of the new IBM devices.
 * IBM devices are now accessed using the `qiskit-ibm-provider <https://github.com/Qiskit/qiskit-ibm-provider>`_ instead of the deprecated :py:class:`IBMQ`. This allows the newest IBM devices and simulators to be accessed through the pytket-qiskit extension.
 * Fix to the `tk_to_qiskit` converter to prevent cancellation of redundant gates when converting to qiskit.
 * Handle qiskit circuits with :py:class:`Initialize` and :py:class:`StatePreparation` instructions in the :py:meth:`qiskit_to_tk` converter. The :py:meth:`tk_to_qiskit` converter now handles :py:class:`StatePreparationBox`.
diff --git a/pytket/extensions/qiskit/backends/ibm.py b/pytket/extensions/qiskit/backends/ibm.py
index 33ab6a83..d5426705 100644
--- a/pytket/extensions/qiskit/backends/ibm.py
+++ b/pytket/extensions/qiskit/backends/ibm.py
@@ -28,6 +28,7 @@
     TYPE_CHECKING,
     Tuple,
     Union,
+    Set,
     Any,
 )
 from warnings import warn
@@ -73,7 +74,9 @@
     CliffordSimp,
     SimplifyInitial,
     NaivePlacementPass,
+    RebaseCustom,
 )
+from pytket.passes._decompositions import _TK1_to_X_SX_Rz
 from pytket.predicates import (  # type: ignore
     NoMidMeasurePredicate,
     NoSymbolsPredicate,
@@ -141,6 +144,29 @@ def _save_ibmq_auth(qiskit_config: Optional[QiskitConfig]) -> None:
             QiskitRuntimeService.save_account(channel="ibm_quantum", token=token)
 
 
+# Variables for defining a rebase to the {X, SX, Rz, ECR} gateset
+# See https://github.com/CQCL/pytket-qiskit/issues/112
+_cx_replacement_with_ecr = (
+    Circuit(2).X(0).SX(1).Rz(-0.5, 0).add_gate(OpType.ECR, [0, 1])
+)
+_tk1_replacement_function = _TK1_to_X_SX_Rz
+
+ecr_rebase = RebaseCustom(
+    gateset={OpType.X, OpType.SX, OpType.Rz, OpType.ECR},
+    cx_replacement=_cx_replacement_with_ecr,
+    tk1_replacement=_tk1_replacement_function,
+)
+
+
+def _get_primitive_gates(gateset: Set[OpType]) -> Set[OpType]:
+    if gateset >= {OpType.X, OpType.SX, OpType.Rz, OpType.CX}:
+        return {OpType.X, OpType.SX, OpType.Rz, OpType.CX}
+    elif gateset >= {OpType.X, OpType.SX, OpType.Rz, OpType.ECR}:
+        return {OpType.X, OpType.SX, OpType.Rz, OpType.ECR}
+    else:
+        return gateset
+
+
 class IBMQBackend(Backend):
     _supports_shots = False
     _supports_counts = True
@@ -176,9 +202,7 @@ def __init__(
         :type token: Optional[str]
         """
         super().__init__()
-        self._pytket_config = (
-            QiskitConfig.from_default_config_file()
-        )  # it looks like this is not working?
+        self._pytket_config = QiskitConfig.from_default_config_file()
         self._provider = (
             self._get_provider(instance=instance, qiskit_config=self._pytket_config)
             if provider is None
@@ -194,7 +218,9 @@ def __init__(
         self._service = QiskitRuntimeService(channel="ibm_quantum", token=token)
         self._session = Session(service=self._service, backend=backend_name)
 
-        self._standard_gateset = gate_set >= {OpType.X, OpType.SX, OpType.Rz, OpType.CX}
+        self._primitive_gates = _get_primitive_gates(gate_set)
+
+        self._supports_rz = OpType.Rz in self._primitive_gates
 
         self._monitor = monitor
 
@@ -370,7 +396,10 @@ def default_compilation_pass(
         # https://cqcl.github.io/pytket-qiskit/api/index.html#default-compilation
         # Edit this docs source file -> pytket-qiskit/docs/intro.txt
         if optimisation_level == 0:
-            if self._standard_gateset:
+            if self._supports_rz:
+                # If the Rz gate is unsupported then the rebase should be skipped
+                # This prevents an error when compiling to the stabilizer backend
+                # where no TK1 replacement can be found for the rebase.
                 passlist.append(self.rebase_pass())
         elif optimisation_level == 1:
             passlist.append(SynthesiseTket())
@@ -414,7 +443,8 @@ def default_compilation_pass(
                     SynthesiseTket(),
                 ]
             )
-        if self._standard_gateset:
+
+        if self._supports_rz:
             passlist.extend([self.rebase_pass(), RemoveRedundancies()])
         if optimisation_level > 0:
             passlist.append(
@@ -428,9 +458,10 @@ def _result_id_type(self) -> _ResultIdTuple:
         return (str, int, int, str)
 
     def rebase_pass(self) -> BasePass:
-        return auto_rebase_pass(
-            {OpType.CX, OpType.X, OpType.SX, OpType.Rz},
-        )
+        if self._primitive_gates == {OpType.X, OpType.SX, OpType.Rz, OpType.ECR}:
+            return ecr_rebase
+        else:
+            return auto_rebase_pass(self._primitive_gates)
 
     def process_circuits(
         self,
diff --git a/tests/backend_test.py b/tests/backend_test.py
index 911fbce2..6e140969 100644
--- a/tests/backend_test.py
+++ b/tests/backend_test.py
@@ -195,7 +195,7 @@ def test_noise(manila_backend: IBMQBackend) -> None:
     assert shots.shape == (10, 4)
 
 
-@pytest.mark.timeout(None)
+@pytest.mark.flaky(reruns=3, reruns_delay=10)
 @pytest.mark.skipif(skip_remote_tests, reason=REASON)
 def test_process_characterisation(manila_backend: IBMQBackend) -> None:
 
@@ -446,6 +446,7 @@ def test_nshots_batching(manila_backend: IBMQBackend) -> None:
         backend._MACHINE_DEBUG = False
 
 
+@pytest.mark.flaky(reruns=3, reruns_delay=10)
 @pytest.mark.skipif(skip_remote_tests, reason=REASON)
 def test_nshots(manila_emulator_backend: IBMQEmulatorBackend) -> None:
     for b in [AerBackend(), manila_emulator_backend]:
@@ -671,7 +672,7 @@ def test_operator() -> None:
 
 
 # TKET-1432 this was either too slow or consumed too much memory when bugged
-@pytest.mark.timeout(10)
+@pytest.mark.flaky(reruns=3, reruns_delay=10)
 def test_expectation_bug() -> None:
     backend = AerStateBackend()
     # backend.compile_circuit(circuit)
@@ -808,6 +809,7 @@ def test_operator_expectation_value() -> None:
     assert np.isclose(e, 1.0)
 
 
+@pytest.mark.flaky(reruns=3, reruns_delay=10)
 @pytest.mark.skipif(skip_remote_tests, reason=REASON)
 def test_ibmq_emulator(manila_emulator_backend: IBMQEmulatorBackend) -> None:
     assert manila_emulator_backend._noise_model is not None
@@ -1085,6 +1087,7 @@ def test_postprocess(lima_backend: IBMQBackend) -> None:
     b.cancel(h)
 
 
+@pytest.mark.flaky(reruns=3, reruns_delay=10)
 @pytest.mark.skipif(skip_remote_tests, reason=REASON)
 def test_postprocess_emu(manila_emulator_backend: IBMQEmulatorBackend) -> None:
     assert manila_emulator_backend.supports_contextual_optimisation
@@ -1101,7 +1104,7 @@ def test_postprocess_emu(manila_emulator_backend: IBMQEmulatorBackend) -> None:
     assert sum(counts.values()) == 10
 
 
-@pytest.mark.timeout(None)
+@pytest.mark.flaky(reruns=3, reruns_delay=10)
 @pytest.mark.skipif(skip_remote_tests, reason=REASON)
 def test_cloud_stabiliser(simulator_stabilizer_backend: IBMQBackend) -> None:
     c = Circuit(2, 2)
@@ -1127,6 +1130,7 @@ def test_available_devices(ibm_provider: IBMProvider) -> None:
     assert len(backend_info_list) > 0
 
 
+@pytest.mark.flaky(reruns=3, reruns_delay=10)
 @pytest.mark.skipif(skip_remote_tests, reason=REASON)
 def test_backendinfo_serialization1(
     manila_emulator_backend: IBMQEmulatorBackend,
@@ -1181,6 +1185,7 @@ def test_sim_qubit_order() -> None:
     assert np.isclose(abs(s[2]), 1.0)
 
 
+@pytest.mark.flaky(reruns=3, reruns_delay=10)
 @pytest.mark.skipif(skip_remote_tests, reason=REASON)
 def test_requrired_predicates(manila_emulator_backend: IBMQEmulatorBackend) -> None:
     # https://github.com/CQCL/pytket-qiskit/issues/93
@@ -1194,3 +1199,31 @@ def test_requrired_predicates(manila_emulator_backend: IBMQEmulatorBackend) -> N
             + "not satisfy MaxNQubitsPredicate(5)"
             in str(errorinfo)
         )
+
+
+@pytest.mark.flaky(reruns=3, reruns_delay=10)
+@pytest.mark.skipif(skip_remote_tests, reason=REASON)
+def test_ecr_gate_compilation(ibm_sherbrooke_backend: IBMQBackend) -> None:
+    assert ibm_sherbrooke_backend.backend_info.gate_set >= {
+        OpType.X,
+        OpType.SX,
+        OpType.Rz,
+        OpType.ECR,
+    }
+    # circuit for an un-routed GHZ state
+    circ = (
+        Circuit(7)
+        .H(0)
+        .CX(0, 1)
+        .CX(0, 2)
+        .CX(0, 3)
+        .CX(0, 4)
+        .CX(0, 5)
+        .CX(0, 6)
+        .measure_all()
+    )
+    for optimisation_level in range(3):
+        compiled_circ = ibm_sherbrooke_backend.get_compiled_circuit(
+            circ, optimisation_level
+        )
+        assert ibm_sherbrooke_backend.valid_circuit(compiled_circ)
diff --git a/tests/conftest.py b/tests/conftest.py
index 6aabcb07..de0e1c70 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -100,3 +100,12 @@ def ibm_provider() -> IBMProvider:
     except:
         token = os.getenv("PYTKET_REMOTE_QISKIT_TOKEN")
         return IBMProvider(token=token, instance="ibm-q/open/main", overwrite=True)
+
+
+@pytest.fixture(scope="module")
+def ibm_sherbrooke_backend() -> IBMQBackend:
+    return IBMQBackend(
+        backend_name="ibm_sherbrooke",
+        monitor=False,
+        token=os.getenv("PYTKET_REMOTE_QISKIT_TOKEN"),
+    )
diff --git a/tests/test-requirements.txt b/tests/test-requirements.txt
index db89364f..bd7f9e32 100644
--- a/tests/test-requirements.txt
+++ b/tests/test-requirements.txt
@@ -1,4 +1,4 @@
 pytest
-pytest-timeout ~= 1.4.2
+pytest-rerunfailures
 hypothesis
 requests_mock

From fde52b7b268ed058cfe7f4381a302fb9cd545552 Mon Sep 17 00:00:00 2001
From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com>
Date: Thu, 22 Jun 2023 14:02:05 +0100
Subject: [PATCH 09/10] Fix IBMQBackend.available_devices bug and update
 credentials documentation (#126)

* fix documentation

* fix bug with IBMProvider being overwritten in available devices

* update tests

* update requirements

* delete legacy file

* fix docstring warning

* fix another doctring issue

* update changelog

* update access and credentials documentation

* black formatting
---
 .github/workflows/docs/intro.txt         | 10 ----------
 .github/workflows/docs/requirements.txt  |  2 +-
 docs/changelog.rst                       |  9 +++++----
 docs/intro.txt                           | 10 ++++++++--
 pytket/extensions/qiskit/backends/ibm.py | 15 ++++++++-------
 tests/backend_test.py                    |  3 +++
 6 files changed, 25 insertions(+), 24 deletions(-)
 delete mode 100644 .github/workflows/docs/intro.txt

diff --git a/.github/workflows/docs/intro.txt b/.github/workflows/docs/intro.txt
deleted file mode 100644
index 85d4a2cc..00000000
--- a/.github/workflows/docs/intro.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-pytket-extensions
-=================
-
-These extensions enable ``pytket` to be used in conjunction with other
-platforms. Each extension adds either new methods to the ``pytket`` package to
-convert between circuit representations, or new backends to which ``pytket``
-circuits can be submitted.
-
-.. _pytket: https://cqcl.github.io/tket/pytket/api/
-.. _Quantinuum: https://www.quantinuum.com/
diff --git a/.github/workflows/docs/requirements.txt b/.github/workflows/docs/requirements.txt
index 3268b4f4..c8d2b6c4 100644
--- a/.github/workflows/docs/requirements.txt
+++ b/.github/workflows/docs/requirements.txt
@@ -1,3 +1,3 @@
-sphinx ~= 4.3.2
+sphinx >= 4.3.2, <6.2.0
 sphinx_book_theme >= 1.0.1, <2.0
 sphinx-copybutton
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 52bc78c0..72e8377a 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -3,11 +3,12 @@ Changelog
 
 0.40.0(unreleased)
 ------------------
+* IBM devices are now accessed using the `qiskit-ibm-provider <https://github.com/Qiskit/qiskit-ibm-provider>`_ instead of the deprecated :py:class:`IBMQ`. This allows the newest IBM devices and simulators to be accessed through ``pytket-qiskit``. See the updated documentation on `credentials <https://cqcl.github.io/pytket-qiskit/api/index.html#access-and-credentials>`_.
+* The parameters ``hub``, ``group`` and ``project`` are no longer handled as separate arguments in :py:class:`IBMQBackend` and :py:meth:`IBMQBackend.available_devices`. Use ``"instance=f"{hub}/{group}/{project}"`` instead.
 * Added support for the {X, SX, Rz, ECR} in the default compilation pass for :py:class:`IBMQBackend` and :py:class:`IBMQEmulatorBackend`. This is the set of gates used by some of the new IBM devices.
-* IBM devices are now accessed using the `qiskit-ibm-provider <https://github.com/Qiskit/qiskit-ibm-provider>`_ instead of the deprecated :py:class:`IBMQ`. This allows the newest IBM devices and simulators to be accessed through the pytket-qiskit extension.
-* Fix to the `tk_to_qiskit` converter to prevent cancellation of redundant gates when converting to qiskit.
+* Fix to the :py:meth:`tk_to_qiskit` converter to prevent cancellation of redundant gates when converting to qiskit.
 * Handle qiskit circuits with :py:class:`Initialize` and :py:class:`StatePreparation` instructions in the :py:meth:`qiskit_to_tk` converter. The :py:meth:`tk_to_qiskit` converter now handles :py:class:`StatePreparationBox`.
-* Fix handling of control state in `qiskit_to_tk`.
+* Fix handling of control state in :py:meth:`qiskit_to_tk`.
 * Update qiskit version to 0.43.1
 * Update qiskit-ibm-runtime version to 0.11.1
 * Update qiskit-ibm-provider version to 0.6.1
@@ -17,7 +18,7 @@ Changelog
 -----------------
 
 * Updated pytket version requirement to 1.15.
-* The get_compiled_circuit method now allows for optional arguments to override the default settings in the NoiseAwarePlacement
+* The :py:meth:`IBMQBackend.get_compiled_circuit` method now allows for optional arguments to override the default settings in the :py:class:`NoiseAwarePlacement`.
 
 0.38.0 (April 2023)
 -------------------
diff --git a/docs/intro.txt b/docs/intro.txt
index d7ea2ad5..e0c70596 100644
--- a/docs/intro.txt
+++ b/docs/intro.txt
@@ -54,6 +54,8 @@ With the exception of the Aer simulators, accessing devices and simulators throu
 
 Once you have created an account you can obtain an API token which you can use to configure your credentials locally.
 
+.. note:: The documentation below is correct as of pytket-qiskit version 0.40.0. In the 0.40.0 release pytket-qiskit moved to using the `qiskit-ibm-provider <https://qiskit.org/ecosystem/ibm-provider/tutorials/Migration_Guide_from_qiskit-ibmq-provider.html>`_. In pytket-qiskit versions 0.39.0 and older the parameters ``hub``, ``group`` and ``project`` were handled separately instead of a single ``instance`` string as in 0.40.0 and newer.
+
 ::
 
     from pytket.extensions.qiskit import set_ibmq_config
@@ -73,6 +75,8 @@ If you are a member of an IBM hub then you can add this information to ``set_ibm
 Alternatively you can use the following qiskit commands to save your credentials
 locally without saving the token in pytket config:
 
+.. note:: If using pytket-qiskit 0.39.0 or older you will have to use the deprecated :py:meth:`IBMQ.save_account` instead of :py:meth:`IBMProvider.save_account` in the code below.
+
 ::
 
     from qiskit_ibm_provider import IBMProvider
@@ -88,9 +92,11 @@ To see which devices you can access you can use the ``available_devices`` method
     from pytket.extensions.qiskit import IBMQBackend
     from qiskit_ibm_provider import IBMProvider
 
-    ibm_provider = IBMProvider()
+    my_instance=f"{hub}/{group}/{project}"
+    ibm_provider = IBMProvider(instance=my_instance)
     backend = IBMQBackend("ibmq_belem") # Initialise backend for an IBM device
-    backend.available_devices(instance=instance=f"{hub}/{group}/{project}", provider=ibm_provider) 
+    backendinfo_list = backend.available_devices(instance=my_instance, provider=ibm_provider) 
+    print([backend.device_name for backend in backendinfo_list])
 
 
 Backends Available Through pytket-qiskit
diff --git a/pytket/extensions/qiskit/backends/ibm.py b/pytket/extensions/qiskit/backends/ibm.py
index d5426705..3f775650 100644
--- a/pytket/extensions/qiskit/backends/ibm.py
+++ b/pytket/extensions/qiskit/backends/ibm.py
@@ -186,12 +186,10 @@ def __init__(
         be specified here as parameters or set in the config file
         using :py:meth:`pytket.extensions.qiskit.set_ibmq_config`.
         This function can also be used to set the IBMQ API token.
+
         :param backend_name: Name of the IBMQ device, e.g. `ibmq_16_melbourne`.
         :type backend_name: str
-        :param hub: Name of the IBMQ hub to use for the provider.
-         If None, just uses the first hub found. Defaults to None.
-        :param instance: A string containing information about the hub/group/project in
-          the following format. Use instance=f"{hub}/{group}/{project}".
+        :param instance: String containing information about the hub/group/project.
         :type instance: str, optional
         :param monitor: Use the IBM job monitor. Defaults to True.
         :type monitor: bool, optional
@@ -316,11 +314,14 @@ def _get_backend_info(cls, backend: "_QiskIBMBackend") -> BackendInfo:
 
     @classmethod
     def available_devices(cls, **kwargs: Any) -> List[BackendInfo]:
-        provider = kwargs.get("provider")
+        provider: Optional["IBMProvider"] = kwargs.get("provider")
         if provider is None:
             if kwargs.get("instance") is not None:
-                provider = cls._get_provider(kwargs.get("instance"), None)
-            provider = IBMProvider()
+                provider = cls._get_provider(
+                    instance=kwargs.get("instance"), qiskit_config=None
+                )
+            else:
+                provider = IBMProvider()
 
         backend_info_list = [
             cls._get_backend_info(backend) for backend in provider.backends()
diff --git a/tests/backend_test.py b/tests/backend_test.py
index 6e140969..0446bcfd 100644
--- a/tests/backend_test.py
+++ b/tests/backend_test.py
@@ -1123,6 +1123,9 @@ def test_available_devices(ibm_provider: IBMProvider) -> None:
     backend_info_list = IBMQBackend.available_devices(instance="ibm-q/open/main")
     assert len(backend_info_list) > 0
 
+    # Check consistency with pytket-qiskit and qiskit provider
+    assert len(backend_info_list) == len(ibm_provider.backends())
+
     backend_info_list = IBMQBackend.available_devices(provider=ibm_provider)
     assert len(backend_info_list) > 0
 

From bcdb062fa8dc2c049a42234bc9a5bb5f2662ed6c Mon Sep 17 00:00:00 2001
From: Melf <cqc@melf.de>
Date: Thu, 22 Jun 2023 14:11:35 +0100
Subject: [PATCH 10/10] update changelog

---
 _metadata.py       | 2 +-
 docs/changelog.rst | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/_metadata.py b/_metadata.py
index 225eaf0f..1432abc8 100644
--- a/_metadata.py
+++ b/_metadata.py
@@ -1,2 +1,2 @@
-__extension_version__ = "0.40.0rc1"
+__extension_version__ = "0.40.0"
 __extension_name__ = "pytket-qiskit"
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 72e8377a..6f4b78b3 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -1,8 +1,9 @@
 Changelog
 ~~~~~~~~~
 
-0.40.0(unreleased)
+0.40.0 (June 2023)
 ------------------
+
 * IBM devices are now accessed using the `qiskit-ibm-provider <https://github.com/Qiskit/qiskit-ibm-provider>`_ instead of the deprecated :py:class:`IBMQ`. This allows the newest IBM devices and simulators to be accessed through ``pytket-qiskit``. See the updated documentation on `credentials <https://cqcl.github.io/pytket-qiskit/api/index.html#access-and-credentials>`_.
 * The parameters ``hub``, ``group`` and ``project`` are no longer handled as separate arguments in :py:class:`IBMQBackend` and :py:meth:`IBMQBackend.available_devices`. Use ``"instance=f"{hub}/{group}/{project}"`` instead.
 * Added support for the {X, SX, Rz, ECR} in the default compilation pass for :py:class:`IBMQBackend` and :py:class:`IBMQEmulatorBackend`. This is the set of gates used by some of the new IBM devices.