From 814ca98a790981536ff5e402d470884a31595b0e Mon Sep 17 00:00:00 2001 From: cqc-melf <70640934+cqc-melf@users.noreply.github.com> Date: Wed, 23 Oct 2024 17:00:45 +0100 Subject: [PATCH 1/9] update os version (#403) --- .github/workflows/build_and_test.yml | 12 ++++++------ .github/workflows/docs.yml | 2 +- .github/workflows/lint.yml | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 5e1719a9..11442a1c 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -25,7 +25,7 @@ jobs: name: Qiskit - Build and test module strategy: matrix: - os: ['ubuntu-24.04', 'macos-14', 'windows-2022'] + os: ['ubuntu-latest', 'macos-14', 'windows-latest'] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 @@ -48,7 +48,7 @@ jobs: with: python-version: '3.11' - name: Build and test including remote checks (3.11) mypy - if: (matrix.os == 'ubuntu-24.04') && (github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || github.event_name == 'release' || github.event_name == 'schedule' ) + if: (matrix.os == 'ubuntu-latest') && (github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || github.event_name == 'release' || github.event_name == 'schedule' ) shell: bash run: | ./.github/workflows/build-test mypy @@ -77,12 +77,12 @@ jobs: - name: Install poetry run: pip install poetry - name: Install docs dependencies - if: (matrix.os == 'ubuntu-24.04') && (github.event_name == 'pull_request' || github.event_name == 'schedule' ) + if: (matrix.os == 'ubuntu-latest') && (github.event_name == 'pull_request' || github.event_name == 'schedule' ) run: | cd docs && bash ./install.sh for w in `find wheelhouse/ -type f -name "*.whl"` ; do poetry install $w ; done - name: Build docs - if: (matrix.os == 'ubuntu-24.04') && (github.event_name == 'pull_request' || github.event_name == 'schedule' ) + if: (matrix.os == 'ubuntu-latest') && (github.event_name == 'pull_request' || github.event_name == 'schedule' ) timeout-minutes: 20 run: | cd docs && poetry run bash ./build-docs.sh @@ -92,7 +92,7 @@ jobs: name: Publish to pypi if: github.event_name == 'release' needs: qiskit-checks - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest steps: - name: Download all wheels # downloading all three files into the wheelhouse @@ -117,7 +117,7 @@ jobs: name: Build docs if: github.event_name == 'release' needs: publish_to_pypi - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 7e88603b..b0191239 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -11,7 +11,7 @@ on: jobs: docs: name: build docs - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 7ee3c13c..e07422f1 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -8,7 +8,7 @@ on: jobs: lint: - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 From 2a767f655b642c331c4f104866609d6f816c7caa Mon Sep 17 00:00:00 2001 From: Isobel Hooper Date: Thu, 24 Oct 2024 11:24:30 +0100 Subject: [PATCH 2/9] Remove ppcirc parameter from qiskit_experimentresult_to_backendresult It's not included in any of the existing calls to this function, and the last time it seems to have been used is by IBMQBackend before the Qiskit API changed (since now IBMQBackend.get_results() handles its own result conversion). --- pytket/extensions/qiskit/result_convert.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pytket/extensions/qiskit/result_convert.py b/pytket/extensions/qiskit/result_convert.py index 2e953966..7bd0e05a 100644 --- a/pytket/extensions/qiskit/result_convert.py +++ b/pytket/extensions/qiskit/result_convert.py @@ -83,7 +83,6 @@ def _result_is_empty_shots(result: ExperimentResult) -> bool: def qiskit_experimentresult_to_backendresult( result: ExperimentResult, - ppcirc: Optional[Circuit] = None, ) -> BackendResult: if not result.success: raise RuntimeError(result.status) @@ -140,7 +139,7 @@ def qiskit_experimentresult_to_backendresult( state=state, unitary=unitary, density_matrix=density_matrix, - ppcirc=ppcirc, + ppcirc=None, ) From 8dbbdf4e799bf8897c8e2caa4253cdfe8d2d5dd9 Mon Sep 17 00:00:00 2001 From: Isobel Hooper Date: Thu, 24 Oct 2024 11:42:50 +0100 Subject: [PATCH 3/9] Add include_foo arguments to qiskit result conversions And when we call those conversion methods from _AerBaseBackend.get_results(), pass what we know about what the backend is expected to support. This should mean that if we run anything on AerStateBackend or AerUnitaryBackend, we don't get counts passed back even if the Qiskit result included them. --- pytket/extensions/qiskit/backends/aer.py | 9 ++++- pytket/extensions/qiskit/result_convert.py | 39 +++++++++++++++++----- tests/qiskit_convert_test.py | 7 +++- 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/pytket/extensions/qiskit/backends/aer.py b/pytket/extensions/qiskit/backends/aer.py index ba64c275..2eaa0aa7 100644 --- a/pytket/extensions/qiskit/backends/aer.py +++ b/pytket/extensions/qiskit/backends/aer.py @@ -348,7 +348,14 @@ def get_result(self, handle: ResultHandle, **kwargs: KwargTypes) -> BackendResul raise CircuitNotRunError(handle) res = job.result() - backresults = qiskit_result_to_backendresult(res) + backresults = qiskit_result_to_backendresult( + res, + include_shots=self._supports_shots, + include_counts=self._supports_counts, + include_state=self._supports_state, + include_unitary=self._supports_unitary, + include_density_matrix=self._supports_density_matrix, + ) for circ_index, backres in enumerate(backresults): self._cache[ResultHandle(jobid, circ_index, qubit_n, ppc)][ "result" diff --git a/pytket/extensions/qiskit/result_convert.py b/pytket/extensions/qiskit/result_convert.py index 7bd0e05a..1ffdf092 100644 --- a/pytket/extensions/qiskit/result_convert.py +++ b/pytket/extensions/qiskit/result_convert.py @@ -81,8 +81,17 @@ def _result_is_empty_shots(result: ExperimentResult) -> bool: return False +# In some cases, Qiskit returns a result with fields we don't expect - +# for example, a circuit with classical bits run on AerStateBackend will +# return counts (whether or not there were measurements). The include_foo +# arguments should be set based on what the backend supports. def qiskit_experimentresult_to_backendresult( result: ExperimentResult, + include_counts: bool = True, + include_shots: bool = True, + include_state: bool = True, + include_unitary: bool = True, + include_density_matrix: bool = True, ) -> BackendResult: if not result.success: raise RuntimeError(result.status) @@ -104,16 +113,16 @@ def qiskit_experimentresult_to_backendresult( shots, counts, state, unitary, density_matrix = (None,) * 5 datadict = result.data.to_dict() - if _result_is_empty_shots(result): + if _result_is_empty_shots(result) and include_shots: n_bits = len(c_bits) if c_bits else 0 shots = OutcomeArray.from_readouts( np.zeros((result.shots, n_bits), dtype=np.uint8) ) else: - if "memory" in datadict: + if "memory" in datadict and include_shots: memory = datadict["memory"] shots = _hex_to_outar(memory, width) - elif "counts" in datadict: + elif "counts" in datadict and include_counts: qis_counts = datadict["counts"] counts = Counter( dict( @@ -122,13 +131,13 @@ def qiskit_experimentresult_to_backendresult( ) ) - if "statevector" in datadict: + if "statevector" in datadict and include_state: state = datadict["statevector"].reverse_qargs().data - if "unitary" in datadict: + if "unitary" in datadict and include_unitary: unitary = datadict["unitary"].reverse_qargs().data - if "density_matrix" in datadict: + if "density_matrix" in datadict and include_density_matrix: density_matrix = datadict["density_matrix"].reverse_qargs().data return BackendResult( @@ -143,9 +152,23 @@ def qiskit_experimentresult_to_backendresult( ) -def qiskit_result_to_backendresult(res: Result) -> Iterator[BackendResult]: +def qiskit_result_to_backendresult( + res: Result, + include_counts: bool = True, + include_shots: bool = True, + include_state: bool = True, + include_unitary: bool = True, + include_density_matrix: bool = True, +) -> Iterator[BackendResult]: for result in res.results: - yield qiskit_experimentresult_to_backendresult(result) + yield qiskit_experimentresult_to_backendresult( + result, + include_counts, + include_shots, + include_state, + include_unitary, + include_density_matrix, + ) def backendresult_to_qiskit_resultdata( diff --git a/tests/qiskit_convert_test.py b/tests/qiskit_convert_test.py index 5dec4e86..e592a6c0 100644 --- a/tests/qiskit_convert_test.py +++ b/tests/qiskit_convert_test.py @@ -549,12 +549,17 @@ def test_convert_result() -> None: qc1.save_state() qisk_result = simulator.run(qc1, shots=10).result() - tk_res = next(qiskit_result_to_backendresult(qisk_result)) + # exclude counts from result (we don't expect them + # for the statevector sim after all) + tk_res = next(qiskit_result_to_backendresult(qisk_result, include_counts=False)) state = tk_res.get_state([Qubit("q2", 1), Qubit("q1", 0), Qubit("q2", 0)]) correct_state = np.zeros(1 << 3, dtype=complex) correct_state[6] = 1 + 0j assert compare_statevectors(state, correct_state) + # also check that we don't return counts in tket result + # even if the qiskit result includes them + assert tk_res._counts is None # check measured qc.measure(qr1[0], cr[0]) From 6f320e2b731828449d7570e8d5aefc71ed758559 Mon Sep 17 00:00:00 2001 From: Isobel Hooper Date: Thu, 24 Oct 2024 11:51:44 +0100 Subject: [PATCH 4/9] Run pylint linting --- pytket/extensions/qiskit/result_convert.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytket/extensions/qiskit/result_convert.py b/pytket/extensions/qiskit/result_convert.py index 1ffdf092..42b38347 100644 --- a/pytket/extensions/qiskit/result_convert.py +++ b/pytket/extensions/qiskit/result_convert.py @@ -26,7 +26,7 @@ from qiskit.result import Result # type: ignore from qiskit.result.models import ExperimentResult # type: ignore -from pytket.circuit import Bit, Qubit, UnitID, Circuit +from pytket.circuit import Bit, Qubit, UnitID from pytket.backends.backendresult import BackendResult from pytket.utils.outcomearray import OutcomeArray From 96d01b1f0a54715651399a663df33651a6a060b4 Mon Sep 17 00:00:00 2001 From: Callum Macpherson <93673602+CalMacCQ@users.noreply.github.com> Date: Thu, 24 Oct 2024 14:05:01 +0100 Subject: [PATCH 5/9] refactor: minor improvement to `qiskit_to_tk` conversion. (#404) * use OpType.StatePreparationBox * refactor circbox conversion --------- Co-authored-by: cqc-melf <70640934+cqc-melf@users.noreply.github.com> --- pytket/extensions/qiskit/qiskit_convert.py | 30 ++++++++++------------ 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/pytket/extensions/qiskit/qiskit_convert.py b/pytket/extensions/qiskit/qiskit_convert.py index cadea2ae..a5654dc9 100644 --- a/pytket/extensions/qiskit/qiskit_convert.py +++ b/pytket/extensions/qiskit/qiskit_convert.py @@ -464,6 +464,16 @@ def _get_pytket_condition_kwargs( raise NotImplementedError("condition must contain classical bit or register") +def _build_circbox(instr: Instruction, circuit: QuantumCircuit) -> CircBox: + qregs = [QuantumRegister(instr.num_qubits, "q")] if instr.num_qubits > 0 else [] + cregs = [ClassicalRegister(instr.num_clbits, "c")] if instr.num_clbits > 0 else [] + builder = CircuitBuilder(qregs, cregs) + builder.add_qiskit_data(circuit, instr.definition) + subc = builder.circuit() + subc.name = instr.name + return CircBox(subc) + + class CircuitBuilder: def __init__( self, @@ -524,7 +534,7 @@ def add_qiskit_data( q_ctrl_box = _get_qcontrol_box(c_gate=instr, params=params) self.tkc.add_qcontrolbox(q_ctrl_box, qubits) - elif isinstance(instr, (Initialize, StatePreparation)): + elif optype == OpType.StatePreparationBox: # Append OpType found by stateprep helpers _add_state_preparation(self.tkc, qubits, instr) @@ -552,22 +562,10 @@ def add_qiskit_data( elif optype == OpType.Barrier: self.tkc.add_barrier(qubits) + elif optype == OpType.CircBox: - qregs = ( - [QuantumRegister(instr.num_qubits, "q")] - if instr.num_qubits > 0 - else [] - ) - cregs = ( - [ClassicalRegister(instr.num_clbits, "c")] - if instr.num_clbits > 0 - else [] - ) - builder = CircuitBuilder(qregs, cregs) - builder.add_qiskit_data(circuit, instr.definition) - subc = builder.circuit() - subc.name = instr.name - self.tkc.add_circbox(CircBox(subc), qubits + bits, **condition_kwargs) # type: ignore + circbox = _build_circbox(instr, circuit) + self.tkc.add_circbox(circbox, qubits + bits, **condition_kwargs) # type: ignore elif optype == OpType.CU3 and type(instr) is qiskit_gates.CUGate: if instr.params[-1] == 0: From 4af1cbfa0563b5c9c57246c23cd472b3ad967ea2 Mon Sep 17 00:00:00 2001 From: cqc-melf <70640934+cqc-melf@users.noreply.github.com> Date: Fri, 25 Oct 2024 09:38:16 +0100 Subject: [PATCH 6/9] Melf/add ruff 2024 (#397) * add ruff config file * add ruff to lint ci check * ruff fixes I * ruff fix II * fix setup path * ruff updates III * fix * add lamba functions * fix mypy * fix merge --- .github/workflows/lint.yml | 8 +- .pylintrc | 1 - pytket/extensions/qiskit/__init__.py | 10 +- pytket/extensions/qiskit/backends/__init__.py | 4 +- pytket/extensions/qiskit/backends/aer.py | 65 ++++++----- pytket/extensions/qiskit/backends/config.py | 9 +- .../qiskit/backends/crosstalk_model.py | 18 +-- pytket/extensions/qiskit/backends/ibm.py | 83 +++++++------- .../extensions/qiskit/backends/ibm_utils.py | 8 +- .../qiskit/backends/ibmq_emulator.py | 17 +-- pytket/extensions/qiskit/qiskit_convert.py | 103 ++++++++++-------- pytket/extensions/qiskit/result_convert.py | 30 +++-- pytket/extensions/qiskit/tket_backend.py | 27 +++-- pytket/extensions/qiskit/tket_job.py | 19 ++-- pytket/extensions/qiskit/tket_pass.py | 16 +-- ruff.toml | 44 ++++++++ setup.py | 8 +- tests/backend_test.py | 78 ++++++------- tests/conftest.py | 31 +++--- tests/mock_pytket_backend.py | 15 +-- tests/qiskit_backend_test.py | 11 +- tests/qiskit_convert_test.py | 68 ++++++------ 22 files changed, 373 insertions(+), 300 deletions(-) create mode 100644 ruff.toml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e07422f1..21b17e9d 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -19,8 +19,12 @@ jobs: - name: Update pip run: pip install --upgrade pip - name: Install black and pylint - run: pip install black pylint + run: pip install black pylint ruff - name: Check files are formatted with black - run: black --check . + run: | + black --check . + - name: Run ruff + run: | + ruff check . - name: Run pylint run: pylint */ diff --git a/.pylintrc b/.pylintrc index 7b251179..ca202983 100644 --- a/.pylintrc +++ b/.pylintrc @@ -47,7 +47,6 @@ enable= unused-variable, unused-wildcard-import, wildcard-import, - wrong-import-order, wrong-import-position, yield-outside-function diff --git a/pytket/extensions/qiskit/__init__.py b/pytket/extensions/qiskit/__init__.py index 86aef51b..106699ec 100644 --- a/pytket/extensions/qiskit/__init__.py +++ b/pytket/extensions/qiskit/__init__.py @@ -14,17 +14,17 @@ """Module for conversion between IBM Qiskit and tket primitives.""" # _metadata.py is copied to the folder after installation. -from ._metadata import __extension_version__, __extension_name__ +from ._metadata import __extension_name__, __extension_version__ from .backends import ( - IBMQBackend, - NoIBMQCredentialsError, AerBackend, + AerDensityMatrixBackend, AerStateBackend, AerUnitaryBackend, - AerDensityMatrixBackend, + IBMQBackend, IBMQEmulatorBackend, + NoIBMQCredentialsError, ) from .backends.config import set_ibmq_config -from .qiskit_convert import qiskit_to_tk, tk_to_qiskit, process_characterisation +from .qiskit_convert import process_characterisation, qiskit_to_tk, tk_to_qiskit # from .tket_pass import TketPass diff --git a/pytket/extensions/qiskit/backends/__init__.py b/pytket/extensions/qiskit/backends/__init__.py index c87e3bed..6b221855 100644 --- a/pytket/extensions/qiskit/backends/__init__.py +++ b/pytket/extensions/qiskit/backends/__init__.py @@ -13,12 +13,12 @@ # limitations under the License. """Backends for connecting to IBM devices and simulators directly from pytket""" -from .ibm import IBMQBackend, NoIBMQCredentialsError from .aer import ( AerBackend, + AerDensityMatrixBackend, AerStateBackend, AerUnitaryBackend, - AerDensityMatrixBackend, qiskit_aer_backend, ) +from .ibm import IBMQBackend, NoIBMQCredentialsError from .ibmq_emulator import IBMQEmulatorBackend diff --git a/pytket/extensions/qiskit/backends/aer.py b/pytket/extensions/qiskit/backends/aer.py index 2eaa0aa7..09535720 100644 --- a/pytket/extensions/qiskit/backends/aer.py +++ b/pytket/extensions/qiskit/backends/aer.py @@ -13,20 +13,18 @@ # limitations under the License. import itertools +import json +import warnings from collections import defaultdict +from collections.abc import Sequence from dataclasses import dataclass -import json from logging import warning -from typing import Optional, Sequence, Any, cast, TYPE_CHECKING -import warnings +from typing import TYPE_CHECKING, Any, Optional, cast import numpy as np -from qiskit import transpile # type: ignore -from qiskit_aer.noise import NoiseModel # type: ignore -from qiskit.quantum_info.operators import Pauli as qk_Pauli # type: ignore -from qiskit.quantum_info.operators.symplectic.sparse_pauli_op import SparsePauliOp # type: ignore from qiskit_aer import Aer # type: ignore -from qiskit_aer.library import save_expectation_value # type: ignore # pylint: disable=unused-import +from qiskit_aer.noise import NoiseModel # type: ignore + from pytket.architecture import Architecture, FullyConnected from pytket.backends import Backend, CircuitNotRunError, CircuitStatus, ResultHandle from pytket.backends.backendinfo import BackendInfo @@ -34,47 +32,54 @@ from pytket.backends.resulthandle import _ResultIdTuple from pytket.circuit import Circuit, Node, OpType, Qubit from pytket.passes import ( + AutoRebase, BasePass, CliffordSimp, CXMappingPass, DecomposeBoxes, FullPeepholeOptimise, + NaivePlacementPass, SequencePass, SynthesiseTket, - AutoRebase, - NaivePlacementPass, ) from pytket.pauli import Pauli, QubitPauliString from pytket.placement import NoiseAwarePlacement from pytket.predicates import ( ConnectivityPredicate, + DefaultRegisterPredicate, GateSetPredicate, - NoClassicalControlPredicate, NoBarriersPredicate, + NoClassicalControlPredicate, NoFastFeedforwardPredicate, NoSymbolsPredicate, - DefaultRegisterPredicate, Predicate, ) +from pytket.utils import prepare_circuit from pytket.utils.operators import QubitPauliOperator from pytket.utils.results import KwargTypes -from pytket.utils import prepare_circuit +from qiskit import transpile # type: ignore +from qiskit.quantum_info.operators import Pauli as qk_Pauli # type: ignore +from qiskit.quantum_info.operators.symplectic.sparse_pauli_op import ( # type: ignore + SparsePauliOp, +) -from .ibm_utils import _STATUS_MAP, _batch_circuits from .._metadata import __extension_version__ from ..qiskit_convert import ( - tk_to_qiskit, _gate_str_2_optype, + tk_to_qiskit, ) from ..result_convert import qiskit_result_to_backendresult from .crosstalk_model import ( - NoisyCircuitBuilder, CrosstalkParams, + NoisyCircuitBuilder, ) +from .ibm_utils import _STATUS_MAP, _batch_circuits if TYPE_CHECKING: from qiskit_aer import AerJob - from qiskit_aer.backends.aerbackend import AerBackend as QiskitAerBackend # type: ignore + from qiskit_aer.backends.aerbackend import ( # type: ignore + AerBackend as QiskitAerBackend, + ) def _default_q_index(q: Qubit) -> int: @@ -326,14 +331,14 @@ def process_circuits( return cast(list[ResultHandle], handle_list) def cancel(self, handle: ResultHandle) -> None: - job: "AerJob" = self._cache[handle]["job"] + job: AerJob = self._cache[handle]["job"] cancelled = job.cancel() if not cancelled: warning(f"Unable to cancel job {cast(str, handle[0])}") def circuit_status(self, handle: ResultHandle) -> CircuitStatus: self._check_handle_type(handle) - job: "AerJob" = self._cache[handle]["job"] + job: AerJob = self._cache[handle]["job"] ibmstatus = job.status() return CircuitStatus(_STATUS_MAP[ibmstatus], ibmstatus.value) @@ -343,7 +348,7 @@ def get_result(self, handle: ResultHandle, **kwargs: KwargTypes) -> BackendResul except CircuitNotRunError: jobid, _, qubit_n, ppc = handle try: - job: "AerJob" = self._cache[handle]["job"] + job: AerJob = self._cache[handle]["job"] except KeyError: raise CircuitNotRunError(handle) @@ -406,10 +411,8 @@ def get_pauli_expectation_value( """ if self._noise_model: raise RuntimeError( - ( - "Snapshot based expectation value not supported with noise model. " - "Use shots." - ) + "Snapshot based expectation value not supported with noise model. " + "Use shots." ) if not self._supports_expectation: raise NotImplementedError("Cannot get expectation value from this backend") @@ -436,10 +439,8 @@ def get_operator_expectation_value( """ if self._noise_model: raise RuntimeError( - ( - "Snapshot based expectation value not supported with noise model. " - "Use shots." - ) + "Snapshot based expectation value not supported with noise model. " + "Use shots." ) if not self._supports_expectation: raise NotImplementedError("Cannot get expectation value from this backend") @@ -754,11 +755,9 @@ def _process_noise_model( raise RuntimeWarning("Error applies to multiple gates.") if "gate_qubits" not in error: raise RuntimeWarning( - ( - "Please define NoiseModel without using the" - " add_all_qubit_quantum_error()" - " or add_all_qubit_readout_error() method." - ) + "Please define NoiseModel without using the" + " add_all_qubit_quantum_error()" + " or add_all_qubit_readout_error() method." ) name = name[0] diff --git a/pytket/extensions/qiskit/backends/config.py b/pytket/extensions/qiskit/backends/config.py index 5ecedf72..04abd0f9 100644 --- a/pytket/extensions/qiskit/backends/config.py +++ b/pytket/extensions/qiskit/backends/config.py @@ -12,8 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any, ClassVar, Optional, Type from dataclasses import dataclass +from typing import Any, ClassVar, Optional + from pytket.config import PytketExtConfig @@ -28,11 +29,11 @@ class QiskitConfig(PytketExtConfig): @classmethod def from_extension_dict( - cls: Type["QiskitConfig"], ext_dict: dict[str, Any] + cls: type["QiskitConfig"], ext_dict: dict[str, Any] ) -> "QiskitConfig": return cls( - ext_dict.get("instance", None), - ext_dict.get("ibmq_api_token", None), + ext_dict.get("instance"), + ext_dict.get("ibmq_api_token"), ) diff --git a/pytket/extensions/qiskit/backends/crosstalk_model.py b/pytket/extensions/qiskit/backends/crosstalk_model.py index fe678968..1d6eaf6e 100644 --- a/pytket/extensions/qiskit/backends/crosstalk_model.py +++ b/pytket/extensions/qiskit/backends/crosstalk_model.py @@ -13,27 +13,29 @@ # limitations under the License. -from typing import Optional from dataclasses import dataclass - -from qiskit_aer.noise import NoiseModel # type: ignore -from qiskit_aer.noise.errors.standard_errors import amplitude_damping_error, phase_damping_error # type: ignore +from typing import Optional import numpy as np +from qiskit_aer.noise import NoiseModel # type: ignore +from qiskit_aer.noise.errors.standard_errors import ( # type: ignore + amplitude_damping_error, + phase_damping_error, +) from scipy.linalg import fractional_matrix_power # type: ignore +from pytket.backends.backendinfo import BackendInfo from pytket.circuit import ( Circuit, - Qubit, - Node, Command, - OpType, + Node, Op, + OpType, + Qubit, Unitary1qBox, Unitary2qBox, Unitary3qBox, ) -from pytket.backends.backendinfo import BackendInfo from pytket.extensions.qiskit.qiskit_convert import _gate_str_2_optype diff --git a/pytket/extensions/qiskit/backends/ibm.py b/pytket/extensions/qiskit/backends/ibm.py index 8669ac32..e0b9384d 100644 --- a/pytket/extensions/qiskit/backends/ibm.py +++ b/pytket/extensions/qiskit/backends/ibm.py @@ -13,83 +13,85 @@ # limitations under the License. import itertools +import json from ast import literal_eval from collections import Counter -import json +from collections.abc import Sequence from time import sleep from typing import ( - cast, - Optional, - Sequence, TYPE_CHECKING, Any, + Optional, + cast, ) from warnings import warn import numpy as np - -from qiskit.primitives import PrimitiveResult, SamplerResult # type: ignore - - -# RuntimeJob has no queue_position attribute, which is referenced -# via job_monitor see-> https://github.com/CQCL/pytket-qiskit/issues/48 -# therefore we can't use job_monitor until fixed -# from qiskit.tools.monitor import job_monitor # type: ignore -from qiskit.result.distributions import QuasiDistribution # type: ignore from qiskit_ibm_runtime import ( # type: ignore QiskitRuntimeService, - Session, + RuntimeJob, SamplerOptions, SamplerV2, - RuntimeJob, + Session, +) +from qiskit_ibm_runtime.models.backend_configuration import ( # type: ignore + PulseBackendConfiguration, +) +from qiskit_ibm_runtime.models.backend_properties import ( # type: ignore + BackendProperties, ) -from qiskit_ibm_runtime.models.backend_configuration import PulseBackendConfiguration # type: ignore -from qiskit_ibm_runtime.models.backend_properties import BackendProperties # type: ignore -from pytket.circuit import Bit, Circuit, OpType +from pytket.architecture import Architecture, FullyConnected from pytket.backends import Backend, CircuitNotRunError, CircuitStatus, ResultHandle from pytket.backends.backendinfo import BackendInfo from pytket.backends.backendresult import BackendResult from pytket.backends.resulthandle import _ResultIdTuple +from pytket.circuit import Bit, Circuit, OpType from pytket.passes import ( - BasePass, AutoRebase, - KAKDecomposition, - RemoveRedundancies, - SequencePass, - SynthesiseTket, + BasePass, + CliffordSimp, CXMappingPass, DecomposeBoxes, FullPeepholeOptimise, - CliffordSimp, - SimplifyInitial, + KAKDecomposition, NaivePlacementPass, + RemoveRedundancies, + SequencePass, + SimplifyInitial, + SynthesiseTket, ) +from pytket.placement import NoiseAwarePlacement from pytket.predicates import ( - NoMidMeasurePredicate, - NoSymbolsPredicate, + DirectednessPredicate, GateSetPredicate, + MaxNQubitsPredicate, NoClassicalControlPredicate, NoFastFeedforwardPredicate, - MaxNQubitsPredicate, + NoMidMeasurePredicate, + NoSymbolsPredicate, Predicate, - DirectednessPredicate, ) - -from pytket.architecture import FullyConnected, Architecture -from pytket.placement import NoiseAwarePlacement from pytket.utils import prepare_circuit from pytket.utils.outcomearray import OutcomeArray from pytket.utils.results import KwargTypes -from .ibm_utils import _STATUS_MAP, _batch_circuits -from .config import QiskitConfig -from ..qiskit_convert import tk_to_qiskit, _tk_gate_set +from qiskit.primitives import PrimitiveResult, SamplerResult # type: ignore + +# RuntimeJob has no queue_position attribute, which is referenced +# via job_monitor see-> https://github.com/CQCL/pytket-qiskit/issues/48 +# therefore we can't use job_monitor until fixed +# from qiskit.tools.monitor import job_monitor # type: ignore +from qiskit.result.distributions import QuasiDistribution # type: ignore + +from .._metadata import __extension_version__ from ..qiskit_convert import ( + _tk_gate_set, get_avg_characterisation, process_characterisation_from_config, + tk_to_qiskit, ) - -from .._metadata import __extension_version__ +from .config import QiskitConfig +from .ibm_utils import _STATUS_MAP, _batch_circuits if TYPE_CHECKING: from qiskit_ibm_runtime.ibm_backend import IBMBackend # type: ignore @@ -183,7 +185,7 @@ def __init__( if service is None else service ) - self._backend: "IBMBackend" = self._service.backend(backend_name) + self._backend: IBMBackend = self._service.backend(backend_name) config: PulseBackendConfiguration = self._backend.configuration() self._max_per_job = getattr(config, "max_experiments", 1) @@ -549,7 +551,7 @@ def process_circuits( handle_list[ind] = ResultHandle( job_id, i, qcs[i].count_ops()["measure"], ppcirc_strs[i] ) - batch_id += 1 + batch_id += 1 # noqa: SIM113 for handle in handle_list: assert handle is not None self._cache[handle] = dict() @@ -607,10 +609,9 @@ def get_result(self, handle: ResultHandle, **kwargs: KwargTypes) -> BackendResul status = job.status() while status not in ["DONE", "CANCELLED", "ERROR"]: status = job.status() - print("Job status is", status) sleep(10) - res = job.result(timeout=kwargs.get("timeout", None)) + res = job.result(timeout=kwargs.get("timeout")) if isinstance(res, SamplerResult): # TODO Is this code still reachable? for circ_index, (r, d) in enumerate(zip(res.quasi_dists, res.metadata)): diff --git a/pytket/extensions/qiskit/backends/ibm_utils.py b/pytket/extensions/qiskit/backends/ibm_utils.py index 19292c76..08055c42 100644 --- a/pytket/extensions/qiskit/backends/ibm_utils.py +++ b/pytket/extensions/qiskit/backends/ibm_utils.py @@ -16,13 +16,13 @@ """ import itertools -from typing import Collection, Optional, Sequence, TYPE_CHECKING +from collections.abc import Collection, Sequence +from typing import TYPE_CHECKING, Optional import numpy as np -from qiskit.providers import JobStatus # type: ignore - from pytket.backends.status import StatusEnum +from qiskit.providers import JobStatus # type: ignore if TYPE_CHECKING: from pytket.circuit import Circuit @@ -61,7 +61,7 @@ def _batch_circuits( n_shots_int = list(map(lambda x: x if x is not None else -1, n_shots)) order: Collection[int] = np.argsort(n_shots_int) - batches: list[tuple[Optional[int], list["Circuit"]]] = [ + batches: list[tuple[Optional[int], list[Circuit]]] = [ (n, [circuits[i] for i in indices]) for n, indices in itertools.groupby(order, key=lambda i: n_shots[i]) ] diff --git a/pytket/extensions/qiskit/backends/ibmq_emulator.py b/pytket/extensions/qiskit/backends/ibmq_emulator.py index 77b25e61..ed33ab25 100644 --- a/pytket/extensions/qiskit/backends/ibmq_emulator.py +++ b/pytket/extensions/qiskit/backends/ibmq_emulator.py @@ -12,22 +12,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -from collections import Counter +from collections.abc import Sequence from typing import ( - Optional, - Sequence, + TYPE_CHECKING, Any, + Optional, ) from qiskit_aer.noise.noise_model import NoiseModel # type: ignore -from qiskit_ibm_runtime import QiskitRuntimeService # type: ignore from pytket.backends.backend import Backend -from pytket.backends.status import CircuitStatus - from pytket.backends.backendinfo import BackendInfo from pytket.backends.backendresult import BackendResult -from pytket.backends.resulthandle import _ResultIdTuple, ResultHandle +from pytket.backends.resulthandle import ResultHandle, _ResultIdTuple +from pytket.backends.status import CircuitStatus from pytket.circuit import Circuit from pytket.passes import BasePass from pytket.predicates import Predicate @@ -36,6 +34,11 @@ from .aer import AerBackend from .ibm import IBMQBackend +if TYPE_CHECKING: + from collections import Counter + + from qiskit_ibm_runtime import QiskitRuntimeService # type: ignore + class IBMQEmulatorBackend(Backend): """A backend which uses the AerBackend to loaclly emulate the behaviour of diff --git a/pytket/extensions/qiskit/qiskit_convert.py b/pytket/extensions/qiskit/qiskit_convert.py index a5654dc9..49dcb584 100644 --- a/pytket/extensions/qiskit/qiskit_convert.py +++ b/pytket/extensions/qiskit/qiskit_convert.py @@ -16,25 +16,55 @@ """Methods to allow conversion between Qiskit and pytket circuit classes """ from collections import defaultdict +from collections.abc import Iterable +from inspect import signature from typing import ( + TYPE_CHECKING, + Any, Callable, Optional, - Any, - Iterable, - cast, TypeVar, - TYPE_CHECKING, + cast, ) -from inspect import signature from uuid import UUID import numpy as np +import sympy from numpy.typing import NDArray +from qiskit_ibm_runtime.models.backend_configuration import ( # type: ignore + PulseBackendConfiguration, +) +from qiskit_ibm_runtime.models.backend_properties import ( # type: ignore + BackendProperties, +) from symengine import sympify # type: ignore from symengine.lib import symengine_wrapper # type: ignore -import sympy import qiskit.circuit.library.standard_gates as qiskit_gates # type: ignore +from pytket.architecture import Architecture, FullyConnected +from pytket.circuit import ( + Bit, + CircBox, + Circuit, + Node, + Op, + OpType, + QControlBox, + Qubit, + StatePreparationBox, + Unitary1qBox, + Unitary2qBox, + Unitary3qBox, + UnitType, +) +from pytket.passes import AutoRebase +from pytket.pauli import Pauli, QubitPauliString +from pytket.unit_id import _TEMP_BIT_NAME +from pytket.utils import ( + QubitPauliOperator, + gen_term_sequence_circuit, + permute_rows_cols_in_unitary, +) from qiskit import ( ClassicalRegister, QuantumCircuit, @@ -42,57 +72,31 @@ ) from qiskit.circuit import ( Barrier, + Clbit, + ControlledGate, + Gate, Instruction, InstructionSet, - Gate, - ControlledGate, Measure, Parameter, ParameterExpression, Reset, - Clbit, ) from qiskit.circuit.library import ( CRYGate, - RYGate, + Initialize, PauliEvolutionGate, + RYGate, StatePreparation, UnitaryGate, - Initialize, -) -from qiskit_ibm_runtime.models.backend_configuration import PulseBackendConfiguration # type: ignore -from qiskit_ibm_runtime.models.backend_properties import BackendProperties # type: ignore - -from pytket.circuit import ( - CircBox, - Circuit, - Node, - Op, - OpType, - Unitary1qBox, - Unitary2qBox, - Unitary3qBox, - UnitType, - Bit, - Qubit, - QControlBox, - StatePreparationBox, ) -from pytket.unit_id import _TEMP_BIT_NAME -from pytket.pauli import Pauli, QubitPauliString -from pytket.architecture import Architecture, FullyConnected -from pytket.utils import ( - QubitPauliOperator, - gen_term_sequence_circuit, - permute_rows_cols_in_unitary, -) -from pytket.passes import AutoRebase if TYPE_CHECKING: from qiskit_ibm_runtime.ibm_backend import IBMBackend # type: ignore from qiskit_ibm_runtime.models.backend_properties import Nduv + + from pytket.circuit import UnitID from qiskit.circuit.quantumcircuitdata import QuantumCircuitData # type: ignore - from pytket.circuit import Op, UnitID _qiskit_gates_1q = { # Exact equivalents (same signature except for factor of pi in each parameter): @@ -233,7 +237,7 @@ def _qpo_from_peg(peg: PauliEvolutionGate, qubits: list[Qubit]) -> QubitPauliOpe qpodict = {} for p, c in zip(op.paulis, op.coeffs): if np.iscomplex(c): - raise ValueError("Coefficient for Pauli {} is non-real.".format(p)) + raise ValueError(f"Coefficient for Pauli {p} is non-real.") coeff = param_to_tk(t) * c qpslist = [] pstr = p.to_label() @@ -891,7 +895,7 @@ def tk_to_qiskit( cregmap.update({c_reg.name: qis_reg}) qcirc.add_register(qis_reg) symb_map = {Parameter(str(s)): s for s in tkc.free_symbols()} - range_preds: dict[Bit, tuple[list["UnitID"], int]] = dict() + range_preds: dict[Bit, tuple[list[UnitID], int]] = dict() # Apply a rebase to the set of pytket gates which have replacements in qiskit supported_gate_rebase.apply(tkc) @@ -1009,8 +1013,12 @@ def return_value_if_found(iterator: Iterable["Nduv"], name: str) -> Optional[Any K2 = TypeVar("K2") V = TypeVar("V") convert_keys_t = Callable[[Callable[[K1], K2], dict[K1, V]], dict[K2, V]] + # convert qubits to architecture Nodes - convert_keys: convert_keys_t = lambda f, d: {f(k): v for k, v in d.items()} + convert_keys: convert_keys_t = lambda f, d: { # noqa: E731 + f(k): v for k, v in d.items() + } + node_errors = convert_keys(lambda q: Node(q), node_errors) link_errors = convert_keys(lambda p: (Node(p[0]), Node(p[1])), link_errors) readout_errors = convert_keys(lambda q: Node(q), readout_errors) @@ -1042,7 +1050,9 @@ def get_avg_characterisation( V1 = TypeVar("V1") V2 = TypeVar("V2") map_values_t = Callable[[Callable[[V1], V2], dict[K, V1]], dict[K, V2]] - map_values: map_values_t = lambda f, d: {k: f(v) for k, v in d.items()} + map_values: map_values_t = lambda f, d: { # noqa: E731 + k: f(v) for k, v in d.items() + } node_errors = cast(dict[Node, dict[OpType, float]], characterisation["NodeErrors"]) link_errors = cast( @@ -1052,10 +1062,13 @@ def get_avg_characterisation( dict[Node, list[list[float]]], characterisation["ReadoutErrors"] ) - avg: Callable[[dict[Any, float]], float] = lambda xs: sum(xs.values()) / len(xs) - avg_mat: Callable[[list[list[float]]], float] = ( + avg: Callable[[dict[Any, float]], float] = lambda xs: sum( # noqa: E731 + xs.values() + ) / len(xs) + avg_mat: Callable[[list[list[float]]], float] = ( # noqa: E731 lambda xs: (xs[0][1] + xs[1][0]) / 2.0 ) + avg_readout_errors = map_values(avg_mat, readout_errors) avg_node_errors = map_values(avg, node_errors) avg_link_errors = map_values(avg, link_errors) diff --git a/pytket/extensions/qiskit/result_convert.py b/pytket/extensions/qiskit/result_convert.py index 42b38347..39c562d1 100644 --- a/pytket/extensions/qiskit/result_convert.py +++ b/pytket/extensions/qiskit/result_convert.py @@ -13,23 +13,20 @@ # limitations under the License. +from collections import Counter, defaultdict +from collections.abc import Iterator, Sequence from typing import ( - Iterator, - Sequence, - Optional, Any, + Optional, ) -from collections import Counter, defaultdict import numpy as np -from qiskit.result import Result # type: ignore -from qiskit.result.models import ExperimentResult # type: ignore - -from pytket.circuit import Bit, Qubit, UnitID - from pytket.backends.backendresult import BackendResult +from pytket.circuit import Bit, Qubit, UnitID from pytket.utils.outcomearray import OutcomeArray +from qiskit.result import Result # type: ignore +from qiskit.result.models import ExperimentResult # type: ignore def _get_registers_from_uids(uids: list[UnitID]) -> dict[str, set[UnitID]]: @@ -71,14 +68,13 @@ def _result_is_empty_shots(result: ExperimentResult) -> bool: return False datadict = result.data.to_dict() - if len(datadict) == 0: - return True - elif "memory" in datadict and len(datadict["memory"]) == 0: - return True - elif "counts" in datadict and len(datadict["counts"]) == 0: - return True - else: - return False + return bool( + len(datadict) == 0 + or "memory" in datadict + and len(datadict["memory"]) == 0 + or "counts" in datadict + and len(datadict["counts"]) == 0 + ) # In some cases, Qiskit returns a result with fields we don't expect - diff --git a/pytket/extensions/qiskit/tket_backend.py b/pytket/extensions/qiskit/tket_backend.py index 47e598bd..a9b46302 100644 --- a/pytket/extensions/qiskit/tket_backend.py +++ b/pytket/extensions/qiskit/tket_backend.py @@ -13,19 +13,22 @@ # limitations under the License. import inspect -from typing import Optional, Any -from qiskit.circuit.library.standard_gates import get_standard_gate_name_mapping # type: ignore +from typing import Any, Optional + +from pytket.architecture import FullyConnected +from pytket.backends import Backend +from pytket.extensions.qiskit import AerStateBackend, AerUnitaryBackend +from pytket.extensions.qiskit.qiskit_convert import _gate_str_2_optype_rev, qiskit_to_tk +from pytket.extensions.qiskit.tket_job import JobInfo, TketJob +from pytket.passes import BasePass +from pytket.predicates import CompilationUnit, GateSetPredicate +from qiskit.circuit.library.standard_gates import ( # type: ignore + get_standard_gate_name_mapping, +) from qiskit.circuit.quantumcircuit import QuantumCircuit # type: ignore -from qiskit.providers.backend import BackendV2 # type: ignore from qiskit.providers import Options # type: ignore +from qiskit.providers.backend import BackendV2 # type: ignore from qiskit.transpiler import CouplingMap, Target # type: ignore -from pytket.extensions.qiskit import AerStateBackend, AerUnitaryBackend -from pytket.extensions.qiskit.qiskit_convert import qiskit_to_tk, _gate_str_2_optype_rev -from pytket.extensions.qiskit.tket_job import TketJob, JobInfo -from pytket.backends import Backend -from pytket.passes import BasePass -from pytket.predicates import GateSetPredicate, CompilationUnit -from pytket.architecture import FullyConnected def _extract_basis_gates(backend: Backend) -> list[str]: @@ -36,7 +39,7 @@ def _extract_basis_gates(backend: Backend) -> list[str]: # Restrict to the gate set accepted by the backend, the converters, # and the Target.from_configuration() method. for optype in pred.gate_set: - if optype in _gate_str_2_optype_rev.keys(): + if optype in _gate_str_2_optype_rev: gate_name = _gate_str_2_optype_rev[optype] if gate_name in standard_gate_mapping: gate_obj = standard_gate_mapping[gate_name] @@ -123,7 +126,7 @@ def run( ) -> TketJob: if isinstance(run_input, QuantumCircuit): run_input = [run_input] - n_shots = options.get("shots", None) + n_shots = options.get("shots") circ_list = [] jobinfos = [] for qc in run_input: diff --git a/pytket/extensions/qiskit/tket_job.py b/pytket/extensions/qiskit/tket_job.py index 2e2dbece..72971832 100644 --- a/pytket/extensions/qiskit/tket_job.py +++ b/pytket/extensions/qiskit/tket_job.py @@ -13,16 +13,17 @@ # limitations under the License. from dataclasses import dataclass -from typing import Optional, Any, TYPE_CHECKING, cast -from qiskit.providers import JobStatus, JobV1 # type: ignore -from qiskit.result import Result # type: ignore +from typing import TYPE_CHECKING, Any, Optional, cast + from pytket.backends import ResultHandle, StatusEnum from pytket.backends.backend import Backend, KwargTypes -from pytket.circuit import UnitID, Qubit, Bit +from pytket.circuit import Bit, Qubit, UnitID from pytket.extensions.qiskit.result_convert import ( - backendresult_to_qiskit_resultdata, _get_header_info, + backendresult_to_qiskit_resultdata, ) +from qiskit.providers import JobStatus, JobV1 # type: ignore +from qiskit.result import Result # type: ignore if TYPE_CHECKING: from pytket.extensions.qiskit.tket_backend import TketBackend @@ -108,13 +109,13 @@ def cancel(self) -> None: def status(self) -> Any: status_list = [self._pytket_backend.circuit_status(h) for h in self._handles] - if any((s.status == StatusEnum.RUNNING for s in status_list)): + if any(s.status == StatusEnum.RUNNING for s in status_list): return JobStatus.RUNNING - elif any((s.status == StatusEnum.ERROR for s in status_list)): + elif any(s.status == StatusEnum.ERROR for s in status_list): return JobStatus.ERROR - elif any((s.status == StatusEnum.CANCELLED for s in status_list)): + elif any(s.status == StatusEnum.CANCELLED for s in status_list): return JobStatus.CANCELLED - elif all((s.status == StatusEnum.COMPLETED for s in status_list)): + elif all(s.status == StatusEnum.COMPLETED for s in status_list): return JobStatus.DONE else: return JobStatus.INITIALIZING diff --git a/pytket/extensions/qiskit/tket_pass.py b/pytket/extensions/qiskit/tket_pass.py index 73071af4..1668fa50 100644 --- a/pytket/extensions/qiskit/tket_pass.py +++ b/pytket/extensions/qiskit/tket_pass.py @@ -13,20 +13,22 @@ # limitations under the License. from typing import Optional -from qiskit.dagcircuit import DAGCircuit # type: ignore -from qiskit.providers import BackendV2 # 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_aer.backends import AerSimulator # type: ignore +from qiskit_aer.backends import AerSimulator # type: ignore -from pytket.passes import BasePass from pytket.extensions.qiskit import ( - IBMQBackend, AerBackend, AerStateBackend, AerUnitaryBackend, + IBMQBackend, ) +from pytket.passes import BasePass +from qiskit.converters import circuit_to_dag, dag_to_circuit # type: ignore +from qiskit.dagcircuit import DAGCircuit # type: ignore +from qiskit.providers import BackendV2 # type: ignore +from qiskit.transpiler.basepasses import BasePass as qBasePass # type: ignore +from qiskit.transpiler.basepasses import TransformationPass + from .qiskit_convert import qiskit_to_tk, tk_to_qiskit diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 00000000..2c68d62c --- /dev/null +++ b/ruff.toml @@ -0,0 +1,44 @@ +target-version = "py39" + +line-length = 88 + +extend-exclude = ["examples"] + +lint.select = [ + "E", # pycodestyle Errors + "W", # pycodestyle Warnings + + # "A", # flake8-builtins + # "B", # flake8-Bugbear + # "C4", # flake8-comprehensions + # "COM", # flake8-commas + # "EXE", # flake8-executable + "F", # pyFlakes + # "FA", # flake8-future-annotations + # "FIX", # flake8-fixme + # "FLY", # flynt + "I", # isort + # "INP", # flake8-no-pep420 + # "ISC", # flake8-implicit-str-concat + # "N", # pep8-Naming + # "NPY", # NumPy-specific + # "PERF", # Perflint + # "PGH", # pygrep-hooks + # "PIE", # flake8-pie + # "PL", # pylint + # "PT", # flake8-pytest-style + # "RSE", # flake8-raise + # "RUF", # Ruff-specific + # "S", # flake8-bandit (Security) + "SIM", # flake8-simplify + # "SLF", # flake8-self + "T20", # flake8-print + "TCH", # flake8-type-checking + # "TRY", # tryceratops + "UP", # pyupgrade + # "YTT", # flake8-2020 +] + +[lint.per-file-ignores] +".github/workflows/docs/conf.py" = ["E402"] +"__init__.py" = ["F401"] # module imported but unused (6) diff --git a/setup.py b/setup.py index 7caed631..949376c0 100644 --- a/setup.py +++ b/setup.py @@ -12,9 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -import shutil import os -from setuptools import setup, find_namespace_packages # type: ignore +import shutil +from pathlib import Path + +from setuptools import find_namespace_packages, setup # type: ignore metadata: dict = {} with open("_metadata.py") as fp: @@ -38,7 +40,7 @@ }, description="Extension for pytket, providing translation to and from the Qiskit " "framework", - long_description=open("README.md").read(), + long_description=(Path(__file__).parent / "README.md").read_text(), long_description_content_type="text/markdown", license="Apache 2", packages=find_namespace_packages(include=["pytket.*"]), diff --git a/tests/backend_test.py b/tests/backend_test.py index ed2617a9..7c165329 100644 --- a/tests/backend_test.py +++ b/tests/backend_test.py @@ -11,73 +11,68 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +import cmath import json +import math import os from collections import Counter from typing import cast -import math -import cmath -from hypothesis import given, strategies -import numpy as np +import numpy as np import pytest - +from hypothesis import given, strategies from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister # type: ignore from qiskit.circuit import Parameter # type: ignore -from qiskit_aer.noise.noise_model import NoiseModel # type: ignore +from qiskit_aer import Aer # type: ignore from qiskit_aer.noise import ReadoutError # type: ignore from qiskit_aer.noise.errors import depolarizing_error, pauli_error # type: ignore +from qiskit_aer.noise.noise_model import NoiseModel # type: ignore from qiskit_ibm_runtime import QiskitRuntimeService # type: ignore -from qiskit_aer import Aer # type: ignore - -from pytket.circuit import ( - Circuit, - OpType, - BasisOrder, - Qubit, - reg_eq, - Unitary2qBox, - QControlBox, - CircBox, -) -from pytket.passes import CliffordSimp, FlattenRelabelRegistersPass -from pytket.pauli import Pauli, QubitPauliString -from pytket.predicates import CompilationUnit, NoMidMeasurePredicate -from pytket.architecture import Architecture -from pytket.mapping import MappingManager, LexiLabellingMethod, LexiRouteRoutingMethod -from pytket.transform import Transform +from pytket.architecture import Architecture, FullyConnected from pytket.backends import ( - ResultHandle, CircuitNotRunError, CircuitNotValidError, CircuitStatus, + ResultHandle, StatusEnum, ) from pytket.backends.backend import ResultHandleTypeError +from pytket.circuit import ( + BasisOrder, + CircBox, + Circuit, + OpType, + QControlBox, + Qubit, + Unitary2qBox, + reg_eq, +) from pytket.extensions.qiskit import ( - IBMQBackend, AerBackend, + AerDensityMatrixBackend, AerStateBackend, AerUnitaryBackend, + IBMQBackend, IBMQEmulatorBackend, - AerDensityMatrixBackend, -) -from pytket.extensions.qiskit import ( + process_characterisation, qiskit_to_tk, tk_to_qiskit, - process_characterisation, ) from pytket.extensions.qiskit.backends.crosstalk_model import ( CrosstalkParams, - NoisyCircuitBuilder, FractionalUnitary, + NoisyCircuitBuilder, ) +from pytket.mapping import LexiLabellingMethod, LexiRouteRoutingMethod, MappingManager +from pytket.passes import CliffordSimp, FlattenRelabelRegistersPass +from pytket.pauli import Pauli, QubitPauliString +from pytket.predicates import CompilationUnit, NoMidMeasurePredicate +from pytket.transform import Transform from pytket.utils.expectations import ( - get_pauli_expectation_value, get_operator_expectation_value, + get_pauli_expectation_value, ) -from pytket.architecture import FullyConnected from pytket.utils.operators import QubitPauliOperator from pytket.utils.results import compare_statevectors, compare_unitaries @@ -147,8 +142,7 @@ def test_statevector_sim_with_permutation() -> None: def test_sim() -> None: c = circuit_gen(True) b = AerBackend() - shots = b.run_circuit(c, n_shots=1024).get_shots() - print(shots) + b.run_circuit(c, n_shots=1024).get_shots() def test_measures() -> None: @@ -395,7 +389,6 @@ def test_cancellation_aer() -> None: c = b.get_compiled_circuit(c) h = b.process_circuit(c, 10) b.cancel(h) - print(b.circuit_status(h)) @pytest.mark.skipif(skip_remote_tests, reason=REASON) @@ -405,7 +398,6 @@ def test_cancellation_ibmq(brisbane_backend: IBMQBackend) -> None: c = b.get_compiled_circuit(c) h = b.process_circuit(c, 10) b.cancel(h) - print(b.circuit_status(h)) @pytest.mark.skipif(skip_remote_tests, reason=REASON) @@ -699,7 +691,7 @@ def test_expectation_bug() -> None: backend = AerStateBackend() # backend.compile_circuit(circuit) circuit = Circuit(16) - with open("big_hamiltonian.json", "r") as f: + with open("big_hamiltonian.json") as f: hamiltonian = QubitPauliOperator.from_list(json.load(f)) exp = backend.get_operator_expectation_value(circuit, hamiltonian) assert np.isclose(exp, 1.4325392) @@ -729,9 +721,11 @@ def test_aer_result_handle() -> None: with pytest.raises(CircuitNotRunError) as errorinfoCirc: _ = b.get_result(wronghandle) - assert "Circuit corresponding to {0!r} ".format( - wronghandle - ) + "has not been run by this backend instance." in str(errorinfoCirc.value) + assert ( + f"Circuit corresponding to {wronghandle!r} " + + "has not been run by this backend instance." + in str(errorinfoCirc.value) + ) def test_aerstate_result_handle() -> None: @@ -780,7 +774,7 @@ def test_mixed_circuit() -> None: backend = AerBackend() c = backend.get_compiled_circuit(c) counts = backend.run_circuit(c, n_shots=1024).get_counts() - for key in counts.keys(): + for key in counts: assert key in {(0, 1), (1, 0)} diff --git a/tests/conftest.py b/tests/conftest.py index e8aa9a90..b9067fb6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -13,9 +13,10 @@ # limitations under the License. import os -import pytest +import pytest from qiskit_ibm_runtime import QiskitRuntimeService # type: ignore + from pytket.extensions.qiskit import ( IBMQBackend, IBMQEmulatorBackend, @@ -24,18 +25,20 @@ @pytest.fixture(autouse=True, scope="session") def setup_qiskit_account() -> None: - if os.getenv("PYTKET_RUN_REMOTE_TESTS") is not None: - # The remote tests require an active IBMQ account - # We check if an IBMQ account is already saved, otherwise we try - # 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 QiskitRuntimeService.saved_accounts(): - token = os.getenv("PYTKET_REMOTE_QISKIT_TOKEN") - if token: - QiskitRuntimeService.save_account( - channel="ibm_quantum", token=token, overwrite=True - ) + # The remote tests require an active IBMQ account + # We check if an IBMQ account is already saved, otherwise we try + # 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 ( + os.getenv("PYTKET_RUN_REMOTE_TESTS") is not None + and not QiskitRuntimeService.saved_accounts() + ): + token = os.getenv("PYTKET_REMOTE_QISKIT_TOKEN") + if token: + QiskitRuntimeService.save_account( + channel="ibm_quantum", token=token, overwrite=True + ) @pytest.fixture(scope="module") @@ -62,7 +65,7 @@ def qiskit_runtime_service() -> QiskitRuntimeService: try: return QiskitRuntimeService(channel="ibm_quantum", instance="ibm-q/open/main") - except: + except: # noqa: E722 token = os.getenv("PYTKET_REMOTE_QISKIT_TOKEN") return QiskitRuntimeService( channel="ibm_quantum", token=token, instance="ibm-q/open/main" diff --git a/tests/mock_pytket_backend.py b/tests/mock_pytket_backend.py index 8afdbd17..13ac0251 100644 --- a/tests/mock_pytket_backend.py +++ b/tests/mock_pytket_backend.py @@ -13,18 +13,19 @@ # limitations under the License. -from typing import Optional, Sequence, cast import json +from collections.abc import Sequence +from typing import Optional, cast -from pytket.circuit import Circuit, OpType +from pytket.architecture import Architecture, FullyConnected from pytket.backends import Backend, CircuitStatus, ResultHandle, StatusEnum +from pytket.backends.backend import KwargTypes, ResultCache from pytket.backends.backendinfo import BackendInfo -from pytket.architecture import Architecture, FullyConnected -from pytket.predicates import Predicate, GateSetPredicate -from pytket.passes import BasePass, CustomPass -from pytket.backends.resulthandle import _ResultIdTuple from pytket.backends.backendresult import BackendResult -from pytket.backends.backend import KwargTypes, ResultCache +from pytket.backends.resulthandle import _ResultIdTuple +from pytket.circuit import Circuit, OpType +from pytket.passes import BasePass, CustomPass +from pytket.predicates import GateSetPredicate, Predicate from pytket.utils.outcomearray import OutcomeArray diff --git a/tests/qiskit_backend_test.py b/tests/qiskit_backend_test.py index fe5fb9b3..0fe745e3 100644 --- a/tests/qiskit_backend_test.py +++ b/tests/qiskit_backend_test.py @@ -16,12 +16,12 @@ import numpy as np import pytest - from qiskit import QuantumCircuit # type: ignore from qiskit.primitives import BackendSamplerV2 # type: ignore from qiskit.providers import JobStatus # type: ignore from qiskit_aer import Aer # type: ignore +from pytket.architecture import Architecture, FullyConnected from pytket.extensions.qiskit import ( AerBackend, AerStateBackend, @@ -29,7 +29,6 @@ IBMQEmulatorBackend, ) from pytket.extensions.qiskit.tket_backend import TketBackend -from pytket.architecture import Architecture, FullyConnected from .mock_pytket_backend import MockShotBackend @@ -55,9 +54,9 @@ def test_samples() -> None: tb = TketBackend(b, comp) job = tb.run(qc, shots=100, memory=True) shots = job.result().get_memory() - assert all(((r[0] == "1" and r[1] == r[2]) for r in shots)) + assert all((r[0] == "1" and r[1] == r[2]) for r in shots) counts = job.result().get_counts() - assert all(((r[0] == "1" and r[1] == r[2]) for r in counts.keys())) + assert all((r[0] == "1" and r[1] == r[2]) for r in counts) def test_state() -> None: @@ -133,6 +132,6 @@ def test_architectures() -> None: tb = TketBackend(b, b.default_compilation_pass()) job = tb.run(qc, shots=100, memory=True) shots = job.result().get_memory() - assert all(((r[0] == "1" and r[1] == r[2]) for r in shots)) + assert all((r[0] == "1" and r[1] == r[2]) for r in shots) counts = job.result().get_counts() - assert all(((r[0] == "1" and r[1] == r[2]) for r in counts.keys())) + assert all((r[0] == "1" and r[1] == r[2]) for r in counts) diff --git a/tests/qiskit_convert_test.py b/tests/qiskit_convert_test.py index e592a6c0..cdde50d3 100644 --- a/tests/qiskit_convert_test.py +++ b/tests/qiskit_convert_test.py @@ -14,61 +14,69 @@ import os from collections import Counter from math import pi -import pytest -from sympy import Symbol + import numpy as np -from qiskit import ( # type: ignore +import pytest +import qiskit.circuit.library.standard_gates as qiskit_gates # type: ignore +from qiskit import ( + ClassicalRegister, QuantumCircuit, QuantumRegister, - ClassicalRegister, transpile, ) -from qiskit.quantum_info import SparsePauliOp, Statevector, Operator # type: ignore -from qiskit.transpiler import PassManager # type: ignore -from qiskit.circuit.library import RYGate, MCMT, XXPlusYYGate, PauliEvolutionGate, UnitaryGate, RealAmplitudes # type: ignore -import qiskit.circuit.library.standard_gates as qiskit_gates # type: ignore from qiskit.circuit import Parameter +from qiskit.circuit.equivalence_library import ( # type: ignore + StandardEquivalenceLibrary, +) +from qiskit.circuit.library import ( + MCMT, + PauliEvolutionGate, + RealAmplitudes, + RYGate, + TwoLocal, + UnitaryGate, + XXPlusYYGate, +) +from qiskit.circuit.parameterexpression import ParameterExpression # type: ignore +from qiskit.quantum_info import Operator, SparsePauliOp, Statevector # type: ignore from qiskit.synthesis import SuzukiTrotter # type: ignore -from qiskit_aer import Aer # type: ignore +from qiskit.transpiler import PassManager # type: ignore from qiskit.transpiler.passes import BasisTranslator # type: ignore -from qiskit.circuit.equivalence_library import StandardEquivalenceLibrary # type: ignore +from qiskit_aer import Aer # type: ignore from qiskit_ibm_runtime.fake_provider import FakeGuadalupeV2 # type: ignore -from qiskit.circuit.parameterexpression import ParameterExpression # type: ignore -from qiskit.circuit.library import TwoLocal -from qiskit import transpile +from sympy import Symbol from pytket.circuit import ( - Circuit, + Bit, CircBox, + Circuit, + CustomGateDef, + Op, + OpType, + QControlBox, + Qubit, + StatePreparationBox, Unitary1qBox, Unitary2qBox, Unitary3qBox, - OpType, - Op, - Qubit, - Bit, - CustomGateDef, reg_eq, - StatePreparationBox, - QControlBox, ) -from pytket.extensions.qiskit import tk_to_qiskit, qiskit_to_tk, IBMQBackend +from pytket.extensions.qiskit import IBMQBackend, qiskit_to_tk, tk_to_qiskit from pytket.extensions.qiskit.backends import qiskit_aer_backend 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 +from pytket.extensions.qiskit.tket_pass import TketAutoPass, TketPass from pytket.passes import ( - RebaseTket, + CliffordSimp, DecomposeBoxes, FullPeepholeOptimise, + RebaseTket, SequencePass, - CliffordSimp, ) - from pytket.utils.results import ( compare_statevectors, - permute_rows_cols_in_unitary, compare_unitaries, + permute_rows_cols_in_unitary, ) skip_remote_tests: bool = os.getenv("PYTKET_RUN_REMOTE_TESTS") is None @@ -105,7 +113,7 @@ def test_parameterised_circuit_global_phase() -> None: qc_2 = tk_to_qiskit(tket_qc) - assert type(qc_2.global_phase) == ParameterExpression + assert type(qc_2.global_phase) is ParameterExpression def test_classical_barrier_error() -> None: @@ -498,7 +506,6 @@ def test_gate_str_2_optype() -> None: "mcx": OpType.CnX, "x": OpType.X, } - print([(_gate_str_2_optype[key], val) for key, val in samples.items()]) assert all(_gate_str_2_optype[key] == val for key, val in samples.items()) @@ -515,7 +522,6 @@ def test_customgate() -> None: qc1 = tk_to_qiskit(circ) newcirc = qiskit_to_tk(qc1) - print(repr(newcirc)) qc2 = tk_to_qiskit(newcirc) correct_circ = Circuit(3).Rx(0.1, 0).Rx(0.4, 2).CZ(0, 1).Rx(0.2, 1) @@ -618,7 +624,7 @@ def assert_tket_circuits_identical(circuits: list[Circuit]) -> None: circ_copies = [] for nn in range(len(circuits)): - assert type(circuits[nn]) == Circuit + assert type(circuits[nn]) is Circuit circ = circuits[nn].copy() circ.name = "tk_circ_must_be_same_name" qbs = circ.qubits From cadaa11f738879b07e0daec7d6ca9f155eec4448 Mon Sep 17 00:00:00 2001 From: Callum Macpherson <93673602+CalMacCQ@users.noreply.github.com> Date: Fri, 25 Oct 2024 11:29:59 +0100 Subject: [PATCH 7/9] fix: bad poetry install command in build and test workflow (#408) --- .github/workflows/build_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 11442a1c..3fd8919d 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -80,7 +80,7 @@ jobs: if: (matrix.os == 'ubuntu-latest') && (github.event_name == 'pull_request' || github.event_name == 'schedule' ) run: | cd docs && bash ./install.sh - for w in `find wheelhouse/ -type f -name "*.whl"` ; do poetry install $w ; done + for w in `find wheelhouse/ -type f -name "*.whl"` ; do poetry run pip install $w ; done - name: Build docs if: (matrix.os == 'ubuntu-latest') && (github.event_name == 'pull_request' || github.event_name == 'schedule' ) timeout-minutes: 20 From 64da26f80ae8dbdc0169ffb12970f3c2668aecb7 Mon Sep 17 00:00:00 2001 From: Alec Edgington <54802828+cqc-alec@users.noreply.github.com> Date: Fri, 25 Oct 2024 11:30:42 +0100 Subject: [PATCH 8/9] Handle bits selection in results for circuits with non-default registers (#407) --- docs/changelog.rst | 5 ++ pytket/extensions/qiskit/backends/ibm.py | 84 +++++++++++++----------- 2 files changed, 49 insertions(+), 40 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index bd579469..5ad57f68 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -3,6 +3,11 @@ Changelog ~~~~~~~~~ +Unreleased +---------- + +* Fix handling of non-default registers when selecting bits in results. + 0.58.0 (October 2024) --------------------- diff --git a/pytket/extensions/qiskit/backends/ibm.py b/pytket/extensions/qiskit/backends/ibm.py index e0b9384d..c21df162 100644 --- a/pytket/extensions/qiskit/backends/ibm.py +++ b/pytket/extensions/qiskit/backends/ibm.py @@ -15,7 +15,7 @@ import itertools import json from ast import literal_eval -from collections import Counter +from collections import Counter, OrderedDict from collections.abc import Sequence from time import sleep from typing import ( @@ -75,14 +75,17 @@ from pytket.utils import prepare_circuit from pytket.utils.outcomearray import OutcomeArray from pytket.utils.results import KwargTypes -from qiskit.primitives import PrimitiveResult, SamplerResult # type: ignore +from qiskit.primitives import ( # type: ignore + BitArray, + DataBin, + PrimitiveResult, + SamplerPubResult, +) # RuntimeJob has no queue_position attribute, which is referenced # via job_monitor see-> https://github.com/CQCL/pytket-qiskit/issues/48 # therefore we can't use job_monitor until fixed # from qiskit.tools.monitor import job_monitor # type: ignore -from qiskit.result.distributions import QuasiDistribution # type: ignore - from .._metadata import __extension_version__ from ..qiskit_convert import ( _tk_gate_set, @@ -99,14 +102,10 @@ _DEBUG_HANDLE_PREFIX = "_MACHINE_DEBUG_" -def _gen_debug_results(n_qubits: int, shots: int, index: int) -> SamplerResult: - debug_dist = {n: 0.0 for n in range(pow(2, n_qubits))} - debug_dist[0] = 1.0 - qd = QuasiDistribution(debug_dist) - return SamplerResult( - quasi_dists=[qd] * (index + 1), - metadata=[{"header_metadata": {}, "shots": shots}] * (index + 1), - ) +def _gen_debug_results(n_bits: int, shots: int) -> PrimitiveResult: + n_u8s = (n_bits - 1) // 8 + 1 + arr = np.array([[0] * n_u8s for _ in range(shots)], dtype=np.uint8) + return PrimitiveResult([SamplerPubResult(DataBin(c=BitArray(arr, n_bits)))]) class NoIBMQCredentialsError(Exception): @@ -205,7 +204,9 @@ def __init__( self._monitor = monitor # cache of results keyed by job id and circuit index - self._ibm_res_cache: dict[tuple[str, int], Counter] = dict() + self._ibm_res_cache: dict[ + tuple[str, int], tuple[Counter, Optional[list[Bit]]] + ] = dict() if sampler_options is None: sampler_options = SamplerOptions() @@ -456,7 +457,7 @@ def default_compilation_pass_offline( @property def _result_id_type(self) -> _ResultIdTuple: - # IBMQ job ID, index, number of measurements per shot, post-processing circuit + # IBMQ job ID, index, number of bits, post-processing circuit return (str, int, int, str) def rebase_pass(self) -> BasePass: @@ -521,14 +522,11 @@ def process_circuits( qcs, ppcirc_strs = [], [] for tkc in batch_chunk: - tkc1 = tkc.copy() - # Flatten bits to default register in lexicographic order: - tkc1.rename_units({bit: Bit(i) for i, bit in enumerate(tkc1.bits)}) if postprocess: - c0, ppcirc = prepare_circuit(tkc1, allow_classical=False) + c0, ppcirc = prepare_circuit(tkc, allow_classical=False) ppcirc_rep = ppcirc.to_dict() else: - c0, ppcirc_rep = tkc1, None + c0, ppcirc_rep = tkc, None if simplify_initial: SimplifyInitial( allow_classical=False, create_all_qubits=True @@ -540,7 +538,7 @@ def process_circuits( handle_list[ind] = ResultHandle( _DEBUG_HANDLE_PREFIX + str((n_shots, batch_id)), i, - batch_chunk[i].n_qubits, + batch_chunk[i].n_bits, ppcirc_strs[i], ) else: @@ -549,7 +547,7 @@ def process_circuits( job_id = job.job_id() for i, ind in enumerate(indices_chunk): handle_list[ind] = ResultHandle( - job_id, i, qcs[i].count_ops()["measure"], ppcirc_strs[i] + job_id, i, qcs[i].num_clbits, ppcirc_strs[i] ) batch_id += 1 # noqa: SIM113 for handle in handle_list: @@ -586,7 +584,7 @@ def get_result(self, handle: ResultHandle, **kwargs: KwargTypes) -> BackendResul cached_result = self._cache[handle] if "result" in cached_result: return cast(BackendResult, cached_result["result"]) - jobid, index, n_meas, ppcirc_str = handle + jobid, index, n_bits, ppcirc_str = handle ppcirc_rep = json.loads(ppcirc_str) ppcirc = Circuit.from_dict(ppcirc_rep) if ppcirc_rep is not None else None cache_key = (jobid, index) @@ -594,7 +592,7 @@ def get_result(self, handle: ResultHandle, **kwargs: KwargTypes) -> BackendResul if self._MACHINE_DEBUG or jobid.startswith(_DEBUG_HANDLE_PREFIX): shots: int shots, _ = literal_eval(jobid[len(_DEBUG_HANDLE_PREFIX) :]) - res = _gen_debug_results(n_meas, shots, index) + res = _gen_debug_results(n_bits, shots) else: try: job = self._retrieve_job(jobid) @@ -612,32 +610,38 @@ def get_result(self, handle: ResultHandle, **kwargs: KwargTypes) -> BackendResul sleep(10) res = job.result(timeout=kwargs.get("timeout")) - if isinstance(res, SamplerResult): - # TODO Is this code still reachable? - for circ_index, (r, d) in enumerate(zip(res.quasi_dists, res.metadata)): - self._ibm_res_cache[(jobid, circ_index)] = Counter( - {n: int(0.5 + d["shots"] * p) for n, p in r.items()} - ) - else: - assert isinstance(res, PrimitiveResult) - for circ_index, pub_result in enumerate(res._pub_results): - readouts = pub_result.data.c.array - self._ibm_res_cache[(jobid, circ_index)] = Counter( - _int_from_readout(readout) for readout in readouts - ) - - counts = self._ibm_res_cache[cache_key] # Counter[int] + assert isinstance(res, PrimitiveResult) + for circ_index, pub_result in enumerate(res._pub_results): + data = pub_result.data + c_regs = OrderedDict( + (reg_name, data.__getattribute__(reg_name).num_bits) + for reg_name in sorted(data.keys()) + ) + readouts = BitArray.concatenate_bits( + [data.__getattribute__(reg_name) for reg_name in c_regs] + ).array + self._ibm_res_cache[(jobid, circ_index)] = ( + Counter(_int_from_readout(readout) for readout in readouts), + list( + itertools.chain.from_iterable( + [Bit(reg_name, i) for i in range(reg_size)] + for reg_name, reg_size in c_regs.items() + ) + ), + ) + + counts, c_bits = self._ibm_res_cache[cache_key] # Counter[int], list[Bit] # Convert to `OutcomeArray`: tket_counts: Counter = Counter() for outcome_key, sample_count in counts.items(): array = OutcomeArray.from_ints( ints=[outcome_key], - width=n_meas, + width=n_bits, big_endian=False, ) tket_counts[array] = sample_count # Convert to `BackendResult`: - result = BackendResult(counts=tket_counts, ppcirc=ppcirc) + result = BackendResult(c_bits=c_bits, counts=tket_counts, ppcirc=ppcirc) self._cache[handle] = {"result": result} return result From b332fbf11c6b924ca336a1e2922c0af4a8135d61 Mon Sep 17 00:00:00 2001 From: Callum Macpherson <93673602+CalMacCQ@users.noreply.github.com> Date: Fri, 25 Oct 2024 12:41:38 +0100 Subject: [PATCH 9/9] docs: port docs source to markdown, update all links (#405) * port source files to markdown * clean up links in table * update ibm sentance * use mystnb code cells * update .gitignore * update all remaining tket.quantinuum.com links * fix invalid table * fix TketBackend links * update theming submodule * update table heading * delete duplicate changelog * update markdown changelog * fix accidental changes to ibm.py * fix a link * use latest submodule version --- .gitignore | 2 + README.md | 4 +- docs/{api.rst => api.md} | 27 +- docs/changelog.md | 359 +++++++++++++++++++ docs/changelog.rst | 417 ----------------------- docs/index.md | 284 +++++++++++++++ docs/index.rst | 240 ------------- docs/pytket-docs-theming | 2 +- pytket/extensions/qiskit/backends/ibm.py | 2 +- pytket/extensions/qiskit/tket_backend.py | 6 +- setup.py | 2 +- 11 files changed, 678 insertions(+), 667 deletions(-) rename docs/{api.rst => api.md} (85%) create mode 100644 docs/changelog.md delete mode 100644 docs/changelog.rst create mode 100644 docs/index.md delete mode 100644 docs/index.rst diff --git a/.gitignore b/.gitignore index 1fe9da90..5919e66e 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ pytket/extensions/qiskit/_metadata.py *.ipynb docs/pyproject.toml docs/poetry.lock +.jupyter_cache/ +jupyter_execute/ \ No newline at end of file diff --git a/README.md b/README.md index b5ef1f87..7f781dc5 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Slack](https://img.shields.io/badge/Slack-4A154B?style=for-the-badge&logo=slack&logoColor=white)](https://tketusers.slack.com/join/shared_invite/zt-18qmsamj9-UqQFVdkRzxnXCcKtcarLRA#) [![Stack Exchange](https://img.shields.io/badge/StackExchange-%23ffffff.svg?style=for-the-badge&logo=StackExchange)](https://quantumcomputing.stackexchange.com/tags/pytket) -[Pytket](https://tket.quantinuum.com/api-docs/index.html) is a python module for interfacing +[Pytket](https://docs.quantinuum.com/tket/api-docs/index.html) is a python module for interfacing with tket, a quantum computing toolkit and optimising compiler developed by Quantinuum. `pytket-qiskit` is an extension to `pytket` that allows `pytket` circuits to be @@ -11,7 +11,7 @@ run on IBM backends and simulators, as well as conversion to and from Qiskit representations. Some useful links: -- [API Documentation](https://tket.quantinuum.com/extensions/pytket-qiskit/) +- [API Documentation](https://docs.quantinuum.com/tket/extensions/pytket-qiskit/) ## Getting started diff --git a/docs/api.rst b/docs/api.md similarity index 85% rename from docs/api.rst rename to docs/api.md index c9283dd2..c3d29e50 100644 --- a/docs/api.rst +++ b/docs/api.md @@ -1,55 +1,78 @@ -API documentation -~~~~~~~~~~~~~~~~~ +# API documentation +```{eval-rst} .. autoclass:: pytket.extensions.qiskit.IBMQBackend :special-members: __init__ :show-inheritance: :members: +``` +```{eval-rst} .. autoclass:: pytket.extensions.qiskit.IBMQEmulatorBackend :special-members: __init__ :show-inheritance: :members: +``` +```{eval-rst} .. autoclass:: pytket.extensions.qiskit.AerBackend :special-members: __init__ :inherited-members: :members: +``` +```{eval-rst} .. autoclass:: pytket.extensions.qiskit.AerStateBackend :special-members: __init__ :inherited-members: :members: +``` +```{eval-rst} .. autoclass:: pytket.extensions.qiskit.AerUnitaryBackend :special-members: __init__ :inherited-members: :members: +``` +```{eval-rst} .. autoclass:: pytket.extensions.qiskit.AerDensityMatrixBackend :special-members: __init__ :inherited-members: :members: +``` +```{eval-rst} .. automodule:: pytket.extensions.qiskit :members: qiskit_to_tk, tk_to_qiskit, process_characterisation +``` +```{eval-rst} .. automodule:: pytket.extensions.qiskit.tket_backend :show-inheritance: :members: :special-members: __init__ +``` +```{eval-rst} .. automodule:: pytket.extensions.qiskit.backends.crosstalk_model :members: CrosstalkParams +``` +```{eval-rst} .. automodule:: pytket.extensions.qiskit.tket_pass :special-members: __init__ :members: +``` +```{eval-rst} .. automodule:: pytket.extensions.qiskit.tket_job :special-members: __init__ :members: +``` +```{eval-rst} .. automodule:: pytket.extensions.qiskit.backends.config :members: +``` diff --git a/docs/changelog.md b/docs/changelog.md new file mode 100644 index 00000000..0e06e304 --- /dev/null +++ b/docs/changelog.md @@ -0,0 +1,359 @@ +```{eval-rst} +.. currentmodule:: pytket.extensions.qiskit +``` + +# Changelog + +## Unreleased + +- Fix handling of non-default registers when selecting bits in results. + +## 0.58.0 (October 2024) + +- Updated pytket version requirement to 1.33.1. +- Require qiskit >= 1.2.4. +- Require qiskit-ibm-runtime >= 0.30.0. +- Require qiskit-aer >= 0.15.1. + +## 0.57.0 (October 2024) + +- Updated pytket version requirement to 1.33. +- Added handling of generalised controlled gates to {py:func}`qiskit_to_tk` and {py:func}`tk_to_qiskit`. The `control_state` is handled directly instead of using additional `X` gates. +- A controlled {py:class}`UnitaryGate` will now be converted to a pytket controlled unitary box by {py:func}`qiskit_to_tk` instead of a controlled {py:class}`~pytket.circuit.CircBox` with a unitary box inside. + +## 0.56.0 (September 2024) + +- Added {py:class}`AerDensityMatrixBackend` simulator. This simulator has the option to support a {py:class}`NoiseModel`. +- Fix conversion of symbols into qiskit. +- Require qiskit >= 1.2.0. +- Add conversion of controlled unitary gates from qiskit to tket. +- Initialize `TketAutoPass` with a `BackendV2`. +- Update `TketBackend` to derive from `BackendV2`. +- Fix to allow `AerBackend` to work with multi-controlled Z gates. + +## 0.55.0 (July 2024) + +- Updated pytket version requirement to 1.30. + +## 0.54.1 (June 2024) + +- Relax version requirements on dependencies by removing upper bounds. +- Fix status reporting when running on hardware. + +## 0.54.0 (June 2024) + +- User can pass a `SamplerOptions` instance (from `qiskit-ibm-runtime`) + via a keyword argument to both an `IBMQBackend` constructor and + an instance method `IBMQBackend.process_circuits`. +- Remove dependency on deprecated qiskit-ibm-provider. +- Remove support for deprecated "ibmq_qasm_simulator" backend. +- Forbid circuits with incomplete classical registers in `tk_to_qiskit()`. +- Updated pytket version requirement to 1.29. +- Update qiskit-ibm-runtime version requirement to 0.24.1. +- Update qiskit version requirement to 1.1. +- Update qiskit-aer version requirement to 0.14.2. +- When constructing an Aer backend with a name for which more than one is + available, emit a warning and pick the first in the list. + +## 0.53.0 (April 2024) + +- Updated pytket version requirement to 1.27. +- Update qiskit-ibm-runtime version requirement to 0.23. + +## 0.52.0 (April 2024) + +- Update pytket version requirement to 1.26. +- Update qiskit-aer version requirement to 0.14. +- Update conversion to qiskit to use symengine for symbolic circuits +- Add `IBMQBackend.default_compilation_pass_offline` for offline compilation given config and props objects. +- Add `DirectednessPredicate` to IBMQBackend +- Default compilation pass of IBMQBackend will keep ECR gates in the direction required by the backend. + +## 0.51.0 (March 2024) + +- Update qiskit-ibm-runtime version requirement to 0.22. +- remove all remote simulators +- rename `IBMQLocalEmulatorBackend` to `IBMQEmulatorBackend` +- `IBMQEmulatorBackend` will now run locally +- add support for contextual optimisation on local emulator + +## 0.50.0 (March 2024) + +- Update qiskit-algorithms version requirement to 0.3. + +## 0.49.0 (March 2024) + +- Update pytket version requirement to 1.25. +- Update qiskit version requirement to 1.0. +- Update qiskit-ibm-provider version requirement to 0.10. +- Update qiskit-ibm-runtime version requirement to 0.21. +- Add `IBMQLocalEmulatorBackend` for running local emulation of + `IBMQBackend` using `AerBackend` with a noise model. + +## 0.48.1rc1 + +- Fix: use correct `instance` with `IBMQEmulatorBackend`. + +## 0.48.0 (January 2024) + +- Updated pytket version requirement to 1.24. +- Python 3.12 support added, 3.9 dropped. +- fix conditional bit in pytket to qiskit conversion +- fix symbolic conversion of parameter in conversion + +## 0.47.0 (January 2024) + +- Update qiskit-ibm-runtime version to 0.17.0. +- Update qiskit-ibm-provider version to 0.8.0. +- Updated pytket version requirement to 1.23. + +## 0.46.0 (November 2023) + +- Updated pytket version requirement to 1.22. +- Add support for circuits with barriers in the Aer simulators. +- Update qiskit version to 0.45.0. +- Update qiskit-ibm-runtime version to 0.15.1. +- Update qiskit-aer version to 0.13.0. +- Update qiskit-ibm-provider version to 0.7.2. +- Introduce dependency on qiskit-algorithms. +- Seed given to `process_circuits()` will be automatically incremented + for the different circuit batches submitted. +- Fix {py:class}`RuntimeError` caused by the use of custom gates in `qiskit_to_tk` [#200](https://github.com/CQCL/pytket-qiskit/issues/200). + +## 0.45.0 (October 2023) + +- Updated pytket version requirement to 1.21. +- Implement crosstalk noise model for AerBackend. +- Don't include `SimplifyInitial` in default passes; instead make it an option + to `process_circuits()`. + +## 0.44.0 (September 2023) + +- Fix to add include Measure, Reset and Conditional operations to the supported operations of {py:class}`AerStateBackend`. +- Update qiskit-ibm-runtime version to 0.12.0. +- Update qiskit-ibm-provider version to 0.7.0. +- Update pytket version requirement to 1.19. + +## 0.43.0 (August 2023) + +- Update qiskit version to 0.44. +- Update qiskit-aer version to 0.12.2. +- Update qiskit-ibm-runtime version to 0.11.3. +- Update qiskit-ibm-provider version to 0.6.3. +- Add option to specify the maximum number of qubits supported by Aer backends + (defaults to 40). + +## 0.42.0 (August 2023) + +- Update pytket version requirement to 1.18. + +## 0.41.0 (July 2023) + +- Update pytket version requirement to 1.17. +- Fix conversion of qiskit `UnitaryGate` to and from pytket (up to 3 qubits). +- Fix handling of qiskit controlled gates in the {py:meth}`qiskit_to_tk` converter. +- Handle CCZ and CSX gates in circuit converters. + +## 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://docs.quantinuum.com/tket/extensions/pytket-qiskit/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. +- 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 {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 +- Update pytket version to 1.16 + +## 0.39.0 (May 2023) + +- Updated pytket version requirement to 1.15. +- 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) + +- Fix to ensure that the {py:class}`IBMBackend` and {py:class}`IBMQEmulatorBackend` both properly enforce {py:class}`MaxNQubitsPredicate`. +- Update qiskit version to 0.42. +- Updated pytket version requirement to 1.14. + +## 0.37.1 (March 2023) + +- Fix backend settings for AerStateBackend and AerUnitaryBackend + +## 0.37.0 (March 2023) + +- Fix faulty information in `AerBackend().backend_info` +- Updated pytket version requirement to 1.13. + +## 0.36.0 (February 2023) + +- Update qiskit version to 0.41. +- Fix order of Pauli terms when converting from `QubitPauliOperator`. + +## 0.35.0 (February 2023) + +- Automatically use IBMQ token if saved in pytket config and not saved in qiskit + config. +- Update qiskit version to 0.40. +- Update code to remove some deprecation warnings. +- Work around . + +## 0.34.0 (January 2023) + +- Handle more multi-controlled gates in `tk_to_qiskit` and `qiskit_to_tk` converters (including CnY and CnZ). +- Drop support for Python 3.8; add support for 3.11. +- Fix ordering of registers in statevector simulation results. +- Remove `reverse_index` argument in `tk_to_qiskit()`. +- Updated pytket version requirement to 1.11. + +## 0.33.0 (December 2022) + +- Fix handling of parameter when converting `PauliEvolutionGate` to + `QubitPauliOperator`. +- Updated pytket version requirement to 1.10. + +## 0.32.0 (December 2022) + +- Use `qiskit_ibm_runtime` services for sampling on `IBMQBackend` and + `IBMQEmulatorBackend`. Note that shots tables (ordered lists of results) are + no longer available from these backends. (`BackendResult.get_shots()` will + fail; use `get_counts()` instead.) +- Fix incorrect circuit permutation handling for `AerUnitaryBackend` and `AerStateBackend`. + +## 0.31.0 (November 2022) + +- Update `TketBackend` to support `FullyConnected` architecture. +- Fix the issue that some qiskit methods can't retrieve results from `TketJob`. +- Updated pytket version requirement to 1.9. +- Handle `OpType.Phase` when converting to qiskit. +- Change default optimization level in `default_compilation_pass()` to 2. + +## 0.30.0 (November 2022) + +- Update qiskit version to 0.39. +- `tk_to_qiskit` now performs a rebase pass prior to conversion. Previously an error was returned if a `Circuit` contained gates such as `OpType.ZZMax` which have no exact replacement in qiskit. Now the unsupported gate will be implemented in terms of gates supported in qiskit rather than returning an error. +- Updated pytket version requirement to 1.8. + +## 0.29.0 (October 2022) + +- Add post-routing `KAKDecomposition` to default pass with `optimisation_level` = 2. +- Add support for `ECRGate` in `tk_to_qiskit` conversion. +- Update qiskit version to 0.38. +- Updated pytket version requirement to 1.7. + +## 0.28.0 (August 2022) + +- Improve result retrieval speed of `AerUnitaryBackend` and `AerStateBackend`. +- Update qiskit version to 0.37. +- Updated pytket version requirement to 1.5. + +## 0.27.0 (July 2022) + +- Updated pytket version requirement to 1.4. + +## 0.26.0 (June 2022) + +- Updated pytket version requirement to 1.3. + +## 0.25.0 (May 2022) + +- Updated pytket version requirement to 1.2. + +## 0.24.0 (April 2022) + +- Fix two-qubit unitary conversions. +- Update qiskit version to 0.36. +- Updated pytket version requirement to 1.1. + +## 0.23.0 (March 2022) + +- Removed `characterisation` property of backends. (Use `backend_info` + instead.) +- Updated pytket version requirement to 1.0. + +## 0.22.2 (February 2022) + +- Fixed {py:meth}`IBMQEmulatorBackend.rebase_pass`. + +## 0.22.1 (February 2022) + +- Added {py:meth}`IBMQEmulatorBackend.rebase_pass`. + +## 0.22.0 (February 2022) + +- Qiskit version updated to 0.34. +- Updated pytket version requirement to 0.19. +- Drop support for Python 3.7; add support for 3.10. + +## 0.21.0 (January 2022) + +- Qiskit version updated to 0.33. +- Updated pytket version requirement to 0.18. + +## 0.20.0 (November 2021) + +- Qiskit version updated to 0.32. +- Updated pytket version requirement to 0.17. + +## 0.19.0 (October 2021) + +- Qiskit version updated to 0.31. +- Removed deprecated {py:meth}`AerUnitaryBackend.get_unitary`. Use + {py:meth}`AerUnitaryBackend.run_circuit` and + {py:meth}`pytket.backends.backendresult.BackendResult.get_unitary` instead. +- Updated pytket version requirement to 0.16. + +## 0.18.0 (September 2021) + +- Qiskit version updated to 0.30. +- Updated pytket version requirement to 0.15. + +## 0.17.0 (September 2021) + +- Updated pytket version requirement to 0.14. + +## 0.16.1 (July 2021) + +- Fix slow/high memory use {py:meth}`AerBackend.get_operator_expectation_value` + +## 0.16.0 (July 2021) + +- Qiskit version updated to 0.28. +- Use provider API client to check job status without retrieving job in IBMQBackend. +- Updated pytket version requirement to 0.13. + +## 0.15.1 (July 2021) + +- Fixed bug in backends when n_shots argument was passed as list. + +## 0.15.0 (June 2021) + +- Updated pytket version requirement to 0.12. + +## 0.14.0 (unreleased) + +- Qiskit version updated to 0.27. + +## 0.13.0 (May 2021) + +- Updated pytket version requirement to 0.11. + +## 0.12.0 (unreleased) + +- Qiskit version updated to 0.26. +- Code rewrites to avoid use of deprecated qiskit methods. +- Restriction to hermitian operators for expectation values in `AerBackend`. + +## 0.11.0 (May 2021) + +- Contextual optimisation added to default compilation passes (except at optimisation level 0). +- Support for symbolic parameters in rebase pass. +- Correct phase when rebasing. +- Ability to preserve UUIDs of qiskit symbolic parameters when converting. +- Correction to error message. + +## 0.10.0 (April 2021) + +- Support for symbolic phase in converters. diff --git a/docs/changelog.rst b/docs/changelog.rst deleted file mode 100644 index 5ad57f68..00000000 --- a/docs/changelog.rst +++ /dev/null @@ -1,417 +0,0 @@ -.. currentmodule:: pytket.extensions.qiskit - -Changelog -~~~~~~~~~ - -Unreleased ----------- - -* Fix handling of non-default registers when selecting bits in results. - -0.58.0 (October 2024) ---------------------- - -* Updated pytket version requirement to 1.33.1. -* Require qiskit >= 1.2.4. -* Require qiskit-ibm-runtime >= 0.30.0. -* Require qiskit-aer >= 0.15.1. - -0.57.0 (October 2024) ---------------------- - -* Updated pytket version requirement to 1.33. -* Added handling of generalised controlled gates to :py:func:`qiskit_to_tk` and :py:func:`tk_to_qiskit`. The `control_state` is handled directly instead of using additional `X` gates. -* A controlled :py:class:`UnitaryGate` will now be converted to a pytket controlled unitary box by :py:func:`qiskit_to_tk` instead of a controlled :py:class:`~pytket.circuit.CircBox` with a unitary box inside. - -0.56.0 (September 2024) ------------------------ - -* Added :py:class:`AerDensityMatrixBackend` simulator. This simulator has the option to support a :py:class:`NoiseModel`. -* Fix conversion of symbols into qiskit. -* Require qiskit >= 1.2.0. -* Add conversion of controlled unitary gates from qiskit to tket. -* Initialize `TketAutoPass` with a `BackendV2`. -* Update `TketBackend` to derive from `BackendV2`. -* Fix to allow `AerBackend` to work with multi-controlled Z gates. - -0.55.0 (July 2024) ------------------- - -* Updated pytket version requirement to 1.30. - -0.54.1 (June 2024) ------------------- - -* Relax version requirements on dependencies by removing upper bounds. -* Fix status reporting when running on hardware. - -0.54.0 (June 2024) ------------------- - -* User can pass a `SamplerOptions` instance (from `qiskit-ibm-runtime`) - via a keyword argument to both an `IBMQBackend` constructor and - an instance method `IBMQBackend.process_circuits`. -* Remove dependency on deprecated qiskit-ibm-provider. -* Remove support for deprecated "ibmq_qasm_simulator" backend. -* Forbid circuits with incomplete classical registers in ``tk_to_qiskit()``. -* Updated pytket version requirement to 1.29. -* Update qiskit-ibm-runtime version requirement to 0.24.1. -* Update qiskit version requirement to 1.1. -* Update qiskit-aer version requirement to 0.14.2. -* When constructing an Aer backend with a name for which more than one is - available, emit a warning and pick the first in the list. - -0.53.0 (April 2024) -------------------- - -* Updated pytket version requirement to 1.27. -* Update qiskit-ibm-runtime version requirement to 0.23. - -0.52.0 (April 2024) -------------------- - -* Update pytket version requirement to 1.26. -* Update qiskit-aer version requirement to 0.14. -* Update conversion to qiskit to use symengine for symbolic circuits -* Add `IBMQBackend.default_compilation_pass_offline` for offline compilation given config and props objects. -* Add `DirectednessPredicate` to IBMQBackend -* Default compilation pass of IBMQBackend will keep ECR gates in the direction required by the backend. - -0.51.0 (March 2024) -------------------- - -* Update qiskit-ibm-runtime version requirement to 0.22. -* remove all remote simulators -* rename ``IBMQLocalEmulatorBackend`` to ``IBMQEmulatorBackend`` -* ``IBMQEmulatorBackend`` will now run locally -* add support for contextual optimisation on local emulator - -0.50.0 (March 2024) -------------------- - -* Update qiskit-algorithms version requirement to 0.3. - -0.49.0 (March 2024) -------------------- - -* Update pytket version requirement to 1.25. -* Update qiskit version requirement to 1.0. -* Update qiskit-ibm-provider version requirement to 0.10. -* Update qiskit-ibm-runtime version requirement to 0.21. -* Add ``IBMQLocalEmulatorBackend`` for running local emulation of - ``IBMQBackend`` using ``AerBackend`` with a noise model. - -0.48.1rc1 ---------- - -* Fix: use correct ``instance`` with ``IBMQEmulatorBackend``. - -0.48.0 (January 2024) ---------------------- - -* Updated pytket version requirement to 1.24. -* Python 3.12 support added, 3.9 dropped. -* fix conditional bit in pytket to qiskit conversion -* fix symbolic conversion of parameter in conversion - -0.47.0 (January 2024) ---------------------- - -* Update qiskit-ibm-runtime version to 0.17.0. -* Update qiskit-ibm-provider version to 0.8.0. -* Updated pytket version requirement to 1.23. - -0.46.0 (November 2023) ----------------------- - -* Updated pytket version requirement to 1.22. -* Add support for circuits with barriers in the Aer simulators. -* Update qiskit version to 0.45.0. -* Update qiskit-ibm-runtime version to 0.15.1. -* Update qiskit-aer version to 0.13.0. -* Update qiskit-ibm-provider version to 0.7.2. -* Introduce dependency on qiskit-algorithms. -* Seed given to ``process_circuits()`` will be automatically incremented - for the different circuit batches submitted. -* Fix :py:class:`RuntimeError` caused by the use of custom gates in ``qiskit_to_tk`` `#200 `_. - -0.45.0 (October 2023) ---------------------- - -* Updated pytket version requirement to 1.21. -* Implement crosstalk noise model for AerBackend. -* Don't include ``SimplifyInitial`` in default passes; instead make it an option - to ``process_circuits()``. - -0.44.0 (September 2023) ------------------------ - -* Fix to add include Measure, Reset and Conditional operations to the supported operations of :py:class:`AerStateBackend`. -* Update qiskit-ibm-runtime version to 0.12.0. -* Update qiskit-ibm-provider version to 0.7.0. -* Update pytket version requirement to 1.19. - -0.43.0 (August 2023) --------------------- - -* Update qiskit version to 0.44. -* Update qiskit-aer version to 0.12.2. -* Update qiskit-ibm-runtime version to 0.11.3. -* Update qiskit-ibm-provider version to 0.6.3. -* Add option to specify the maximum number of qubits supported by Aer backends - (defaults to 40). - -0.42.0 (August 2023) --------------------- - -* Update pytket version requirement to 1.18. - -0.41.0 (July 2023) ------------------- - -* Update pytket version requirement to 1.17. -* Fix conversion of qiskit `UnitaryGate` to and from pytket (up to 3 qubits). -* Fix handling of qiskit controlled gates in the :py:meth:`qiskit_to_tk` converter. -* Handle CCZ and CSX gates in circuit converters. - -0.40.0 (June 2023) ------------------- - -* IBM devices are now accessed using the `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 `_. -* 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. -* 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 :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 -* Update pytket version to 1.16 - -0.39.0 (May 2023) ------------------ - -* Updated pytket version requirement to 1.15. -* 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) -------------------- - -* Fix to ensure that the :py:class:`IBMBackend` and :py:class:`IBMQEmulatorBackend` both properly enforce :py:class:`MaxNQubitsPredicate`. -* Update qiskit version to 0.42. -* Updated pytket version requirement to 1.14. - -0.37.1 (March 2023) -------------------- - -* Fix backend settings for AerStateBackend and AerUnitaryBackend - -0.37.0 (March 2023) -------------------- - -* Fix faulty information in ``AerBackend().backend_info`` -* Updated pytket version requirement to 1.13. - -0.36.0 (February 2023) ----------------------- - -* Update qiskit version to 0.41. -* Fix order of Pauli terms when converting from ``QubitPauliOperator``. - -0.35.0 (February 2023) ----------------------- - -* Automatically use IBMQ token if saved in pytket config and not saved in qiskit - config. -* Update qiskit version to 0.40. -* Update code to remove some deprecation warnings. -* Work around https://github.com/Qiskit/qiskit-terra/issues/7865. - -0.34.0 (January 2023) ---------------------- - -* Handle more multi-controlled gates in ``tk_to_qiskit`` and ``qiskit_to_tk`` converters (including CnY and CnZ). -* Drop support for Python 3.8; add support for 3.11. -* Fix ordering of registers in statevector simulation results. -* Remove ``reverse_index`` argument in ``tk_to_qiskit()``. -* Updated pytket version requirement to 1.11. - -0.33.0 (December 2022) ----------------------- - -* Fix handling of parameter when converting ``PauliEvolutionGate`` to - ``QubitPauliOperator``. -* Updated pytket version requirement to 1.10. - -0.32.0 (December 2022) ----------------------- - -* Use ``qiskit_ibm_runtime`` services for sampling on ``IBMQBackend`` and - ``IBMQEmulatorBackend``. Note that shots tables (ordered lists of results) are - no longer available from these backends. (``BackendResult.get_shots()`` will - fail; use ``get_counts()`` instead.) - -* Fix incorrect circuit permutation handling for ``AerUnitaryBackend`` and ``AerStateBackend``. - -0.31.0 (November 2022) ----------------------- - -* Update ``TketBackend`` to support ``FullyConnected`` architecture. -* Fix the issue that some qiskit methods can't retrieve results from ``TketJob``. -* Updated pytket version requirement to 1.9. -* Handle ``OpType.Phase`` when converting to qiskit. -* Change default optimization level in ``default_compilation_pass()`` to 2. - -0.30.0 (November 2022) ----------------------- - -* Update qiskit version to 0.39. -* ``tk_to_qiskit`` now performs a rebase pass prior to conversion. Previously an error was returned if a ``Circuit`` contained gates such as ``OpType.ZZMax`` which have no exact replacement in qiskit. Now the unsupported gate will be implemented in terms of gates supported in qiskit rather than returning an error. -* Updated pytket version requirement to 1.8. - -0.29.0 (October 2022) ---------------------- - -* Add post-routing ``KAKDecomposition`` to default pass with ``optimisation_level`` = 2. -* Add support for ``ECRGate`` in ``tk_to_qiskit`` conversion. -* Update qiskit version to 0.38. -* Updated pytket version requirement to 1.7. - - -0.28.0 (August 2022) --------------------- - -* Improve result retrieval speed of ``AerUnitaryBackend`` and ``AerStateBackend``. -* Update qiskit version to 0.37. -* Updated pytket version requirement to 1.5. - -0.27.0 (July 2022) ------------------- - -* Updated pytket version requirement to 1.4. - -0.26.0 (June 2022) ------------------- - -* Updated pytket version requirement to 1.3. - -0.25.0 (May 2022) ------------------ - -* Updated pytket version requirement to 1.2. - -0.24.0 (April 2022) -------------------- - -* Fix two-qubit unitary conversions. -* Update qiskit version to 0.36. -* Updated pytket version requirement to 1.1. - -0.23.0 (March 2022) -------------------- - -* Removed ``characterisation`` property of backends. (Use `backend_info` - instead.) -* Updated pytket version requirement to 1.0. - -0.22.2 (February 2022) ----------------------- - -* Fixed :py:meth:`IBMQEmulatorBackend.rebase_pass`. - -0.22.1 (February 2022) ----------------------- - -* Added :py:meth:`IBMQEmulatorBackend.rebase_pass`. - -0.22.0 (February 2022) ----------------------- - -* Qiskit version updated to 0.34. -* Updated pytket version requirement to 0.19. -* Drop support for Python 3.7; add support for 3.10. - -0.21.0 (January 2022) ---------------------- - -* Qiskit version updated to 0.33. -* Updated pytket version requirement to 0.18. - -0.20.0 (November 2021) ----------------------- - -* Qiskit version updated to 0.32. -* Updated pytket version requirement to 0.17. - -0.19.0 (October 2021) ---------------------- - -* Qiskit version updated to 0.31. -* Removed deprecated :py:meth:`AerUnitaryBackend.get_unitary`. Use - :py:meth:`AerUnitaryBackend.run_circuit` and - :py:meth:`pytket.backends.backendresult.BackendResult.get_unitary` instead. -* Updated pytket version requirement to 0.16. - -0.18.0 (September 2021) ------------------------ - -* Qiskit version updated to 0.30. -* Updated pytket version requirement to 0.15. - -0.17.0 (September 2021) ------------------------ - -* Updated pytket version requirement to 0.14. - -0.16.1 (July 2021) ------------------- - -* Fix slow/high memory use :py:meth:`AerBackend.get_operator_expectation_value` - -0.16.0 (July 2021) ------------------- - -* Qiskit version updated to 0.28. -* Use provider API client to check job status without retrieving job in IBMQBackend. -* Updated pytket version requirement to 0.13. - -0.15.1 (July 2021) ------------------- - -* Fixed bug in backends when n_shots argument was passed as list. - -0.15.0 (June 2021) ------------------- - -* Updated pytket version requirement to 0.12. - -0.14.0 (unreleased) -------------------- - -* Qiskit version updated to 0.27. - -0.13.0 (May 2021) ------------------ - -* Updated pytket version requirement to 0.11. - -0.12.0 (unreleased) -------------------- - -* Qiskit version updated to 0.26. -* Code rewrites to avoid use of deprecated qiskit methods. -* Restriction to hermitian operators for expectation values in `AerBackend`. - -0.11.0 (May 2021) ------------------ - -* Contextual optimisation added to default compilation passes (except at optimisation level 0). -* Support for symbolic parameters in rebase pass. -* Correct phase when rebasing. -* Ability to preserve UUIDs of qiskit symbolic parameters when converting. -* Correction to error message. - -0.10.0 (April 2021) -------------------- - -* Support for symbolic phase in converters. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..8911076e --- /dev/null +++ b/docs/index.md @@ -0,0 +1,284 @@ +--- +file_format: mystnb +--- + +# pytket-qiskit + +IBM's [Qiskit](https://www.ibm.com/quantum/qiskit) is an open-source framework for quantum +computation, ranging from high-level algorithms to low-level circuit +representations, simulation and access to the IBM quantum devices and simulators. + +`pytket-qiskit` is an extension to `pytket` that allows `pytket` circuits to be +run on IBM backends and simulators, as well as conversion to and from Qiskit +representations. + +`pytket-qiskit` is available for Python 3.10, 3.11 and 3.12, on Linux, MacOS and +Windows. To install, run: + +``` +pip install pytket-qiskit +``` + +This will install `pytket` if it isn't already installed, and add new classes +and methods into the `pytket.extensions` namespace. + +## Available IBM Backends + +```{eval-rst} +.. currentmodule:: pytket.extensions.qiskit +``` + +```{eval-rst} +.. autosummary:: + :nosignatures: + + IBMQBackend + IBMQEmulatorBackend + AerBackend + AerStateBackend + AerUnitaryBackend + AerDensityMatrixBackend + +``` + +An example using the shots-based {py:class}`AerBackend` simulator is shown below. + +```{code-cell} ipython3 +--- +tags: [skip-execution] +--- +from pytket.extensions.qiskit import AerBackend +from pytket import Circuit + +backend = AerBackend() +circ = Circuit(2).H(0).CX(0, 1).measure_all() + +# Compilation not needed here as both H and CX are supported gates +result = backend.run_circuit(circ, n_shots=1000) +``` + +This simulator supports a large set of gates and by default has no architectural constraints or quantum noise. However the user can pass in a noise model or custom architecture to more closely model a real quantum device. + +The {py:class}`AerBackend` also supports GPU simulation which can be configured as follows. + +```{code-cell} ipython3 +--- +tags: [skip-execution] +--- +from pytket.extensions.qiskit import AerBackend + +backend = AerBackend() +backend._qiskit_backend.set_option("device", "GPU") +``` + +:::{note} +Making use of GPU simulation requires the qiskit-aer-gpu package. This can be installed with the command + +``` +pip install qiskit-aer-gpu +``` +::: + +## Access and Credentials + +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: . + +Once you have created an account you can obtain an API token which you can use to configure your credentials locally. + +In this section we are assuming that you have set the following variables with the corresponding values: + +```{code-cell} ipython3 +--- +tags: [skip-execution] +--- +# Replace the placeholders with your actual values + +ibm_token = '' +hub = '' +group = '' +project = '' + +inst = f"{hub}/{group}/{project}" +``` + +### Method 1: Using {py:class}`QiskitRuntimeService` + +You can use the following qiskit commands to save your IBM credentials +to disk: + +```{code-cell} ipython3 +--- +tags: [skip-execution] +--- +from qiskit_ibm_runtime import QiskitRuntimeService + +QiskitRuntimeService.save_account(channel="ibm_quantum", token=ibm_token, instance=inst) +``` + +To see which devices you can access, use the {py:meth}`IBMQBackend.available_devices` method. Note that it is possible to pass an optional `instance` argument to this method. This allows you to see which IBM devices are accessible with your credentials. + +```{code-cell} ipython3 +--- +tags: [skip-execution] +--- +from pytket.extensions.qiskit import IBMQBackend + +backend = IBMQBackend("ibm_kyiv") # Initialise backend for an IBM device + +backendinfo_list = backend.available_devices(instance=inst) +print([backend.device_name for backend in backendinfo_list]) +``` + +For more information, see the documentation for [qiskit-ibm-runtime](https://docs.quantum.ibm.com/api/qiskit-ibm-runtime). + +### Method 2: Saving credentials in a local pytket config file + +Alternatively, you can store your credentials in local pytket config using the {py:meth}`~pytket.extensions.qiskit.backends.config.set_ibmq_config` method. + +```{code-cell} ipython3 +--- +tags: [skip-execution] +--- +from pytket.extensions.qiskit import set_ibmq_config + +set_ibmq_config(ibmq_api_token=ibm_token) +``` + +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 {py:meth}`~pytket.extensions.qiskit.backends.config.set_ibmq_config` as well. + +```{code-cell} ipython3 +--- +tags: [skip-execution] +--- +from pytket.extensions.qiskit import set_ibmq_config + +set_ibmq_config(ibmq_api_token=ibm_token, instance=f"{hub}/{group}/{project}") +``` + +```{eval-rst} +.. currentmodule:: pytket.extensions.qiskit.backends.config +``` + +```{eval-rst} +.. autosummary:: + :nosignatures: + + QiskitConfig + set_ibmq_config +``` + +## Converting circuits between pytket and qiskit + +Users may wish to port quantum circuits between pytket and qiskit. This allows the features of both libraries to be used. +For instance those familiar with qiskit may wish to convert their circuits to pytket and use the available compilation passes to optimise circuits. + +```{eval-rst} +.. currentmodule:: pytket.extensions.qiskit + +``` + +```{eval-rst} +.. autosummary:: + :nosignatures: + + qiskit_to_tk + tk_to_qiskit + +``` + +## Default Compilation + +Every {py:class}`~pytket.backends.backend.Backend` in pytket has its own {py:meth}`~pytket.backends.Backend.default_compilation_pass` method. This method applies a sequence of optimisations to a circuit depending on the value of an `optimisation_level` parameter. This default compilation will ensure that the circuit meets all the constraints required to run on the {py:class}`~pytket.backends.backend.Backend`. The passes applied by different levels of optimisation are specified in the table below. + +:::{list-table} **Default compilation pass for the IBMQBackend and IBMQEmulatorBackend** +:widths: 25 25 25 +:header-rows: 1 + +* - optimisation_level = 0 + - optimisation_level = 1 + - optimisation_level = 2 [1] +* - [DecomposeBoxes](inv:#*.passes.DecomposeBoxes) + - [DecomposeBoxes](inv:#*.passes.DecomposeBoxes) + - [DecomposeBoxes](inv:#*.passes.DecomposeBoxes) +* - [AutoRebase [2]](inv:#*.AutoRebase) + - [SynthesiseTket](inv:#*.SynthesiseTket) + - [FullPeepholeOptimise](inv:#*.passes.FullPeepholeOptimise) +* - [CXMappingPass [3]](inv:#*.passes.CXMappingPass) + - [CXMappingPass [3]](inv:#*.passes.CXMappingPass) + - [CXMappingPass [3]](inv:#*.passes.CXMappingPass) +* - [NaivePlacementPass](inv:#*.passes.NaivePlacementPass) + - [NaivePlacementPass](inv:#*.passes.NaivePlacementPass) + - [NaivePlacementPass](inv:#*.passes.NaivePlacementPass) +* - [AutoRebase [2]](inv:#*.AutoRebase) + - [SynthesiseTket](inv:#*.SynthesiseTket) + - [KAKDecomposition(allow_swaps=False)](inv:#*.passes.KAKDecomposition) +* - [RemoveRedundancies](inv:#*.passes.RemoveRedundancies) + - [AutoRebase [2]](inv:#*.AutoRebase) + - [CliffordSimp(allow_swaps=False)](inv:#*.passes.CliffordSimp) +* - + - [RemoveRedundancies](inv:#*.passes.RemoveRedundancies) + - [SynthesiseTket](inv:#*.SynthesiseTket) +* - + - + - [AutoRebase [2]](inv:#*.AutoRebase) +* - + - + - [RemoveRedundancies](inv:#*.passes.RemoveRedundancies) + +::: + +- \[1\] If no value is specified then `optimisation_level` defaults to a value of 2. +- \[2\] {py:class}`~pytket._tket.passes.AutoRebase` is a conversion to the gateset supported by the backend. For IBM quantum devices and emulators the supported gate set is either $\{X, SX, Rz, CX\}$, $\{X, SX, Rz, ECR\}$, or $\{X, SX, Rz, CZ\}$. The more idealised Aer simulators have a much broader range of supported gates. +- \[3\] Here [CXMappingPass](inv:#*.passes.CXMappingPass) maps program qubits to the architecture using a [NoiseAwarePlacement](inv:#*.NoiseAwarePlacement) + +**Note:** The {py:meth}`~AerBackend.default_compilation_pass` for {py:class}`AerBackend` is the same as above. + +## Noise Modelling + +```{eval-rst} +.. currentmodule:: pytket.extensions.qiskit.backends.crosstalk_model +``` + +```{eval-rst} +.. autosummary:: + :nosignatures: + + CrosstalkParams + +``` + +## Using TKET directly on qiskit circuits + +```{eval-rst} +.. currentmodule:: pytket.extensions.qiskit +``` + +For usage of {py:class}`~tket_backend.TketBackend` see the [qiskit integration notebook example](https://docs.quantinuum.com/tket/user-guide/examples/backends/qiskit_integration.html). + +```{eval-rst} +.. autosummary:: + :nosignatures: + + ~tket_backend.TketBackend + ~tket_pass.TketPass + ~tket_pass.TketAutoPass + ~tket_job.TketJob + + +``` + +```{eval-rst} +.. toctree:: + api.md + changelog.md +``` + +```{eval-rst} +.. toctree:: + :caption: Useful links + + Issue tracker + PyPi +``` diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index 7d90f7da..00000000 --- a/docs/index.rst +++ /dev/null @@ -1,240 +0,0 @@ -pytket-qiskit -############# - -IBM's `Qiskit `_ is an open-source framework for quantum -computation, ranging from high-level algorithms to low-level circuit -representations, simulation and access to the `IBMQ `_ Experience devices. - -``pytket-qiskit`` is an extension to ``pytket`` that allows ``pytket`` circuits to be -run on IBM backends and simulators, as well as conversion to and from Qiskit -representations. - -``pytket-qiskit`` is available for Python 3.10, 3.11 and 3.12, on Linux, MacOS and -Windows. To install, run: - -:: - - pip install pytket-qiskit - -This will install ``pytket`` if it isn't already installed, and add new classes -and methods into the ``pytket.extensions`` namespace. - -Available IBM Backends -====================== - -.. currentmodule:: pytket.extensions.qiskit - -.. autosummary:: - :nosignatures: - - IBMQBackend - IBMQEmulatorBackend - AerBackend - AerStateBackend - AerUnitaryBackend - AerDensityMatrixBackend - - -An example using the shots-based :py:class:`AerBackend` simulator is shown below. - -:: - - from pytket.extensions.qiskit import AerBackend - from pytket import Circuit - - backend = AerBackend() - circ = Circuit(2).H(0).CX(0, 1).measure_all() - - # Compilation not needed here as both H and CX are supported gates - result = backend.run_circuit(circ, n_shots=1000) - -This simulator supports a large set of gates and by default has no architectural constraints or quantum noise. However the user can pass in a noise model or custom architecture to more closely model a real quantum device. - -The :py:class:`AerBackend` also supports GPU simulation which can be configured as follows. - -:: - - from pytket.extensions.qiskit import AerBackend - - backend = AerBackend() - backend._qiskit_backend.set_option("device", "GPU") - -.. note:: Making use of GPU simulation requires the qiskit-aer-gpu package. This can be installed with the command - :: - - pip install qiskit-aer-gpu - -Access and Credentials -====================== - -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.ibm.com/. - -Once you have created an account you can obtain an API token which you can use to configure your credentials locally. - -In this section we are assuming that you have set the following variables with the corresponding values: - -:: - - # Replace the placeholders with your actual values - - ibm_token = '' - hub = '' - group = '' - project = '' - - inst = f"{hub}/{group}/{project}" - -Method 1: Using :py:class:`QiskitRuntimeService` ------------------------------------------------- - -You can use the following qiskit commands to save your IBM credentials -to disk: - -:: - - from qiskit_ibm_runtime import QiskitRuntimeService - - QiskitRuntimeService.save_account(channel="ibm_quantum", token=ibm_token, instance=inst) - -To see which devices you can access, use the :py:meth:`IBMQBackend.available_devices` method. Note that it is possible to pass an optional ``instance`` argument to this method. This allows you to see which IBM devices are accessible with your credentials. - -:: - - from pytket.extensions.qiskit import IBMQBackend - - backend = IBMQBackend("ibm_kyiv") # Initialise backend for an IBM device - - backendinfo_list = backend.available_devices(instance=inst) - print([backend.device_name for backend in backendinfo_list]) - -For more information, see the documentation for `qiskit-ibm-runtime `_. - - -Method 2: Saving credentials in a local pytket config file ----------------------------------------------------------- -Alternatively, you can store your credentials in local pytket config using the :py:meth:`~pytket.extensions.qiskit.backends.config.set_ibmq_config` method. - -:: - - from pytket.extensions.qiskit import set_ibmq_config - - set_ibmq_config(ibmq_api_token=ibm_token) - -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 :py:meth:`~pytket.extensions.qiskit.backends.config.set_ibmq_config` as well. - -:: - - from pytket.extensions.qiskit import set_ibmq_config - - set_ibmq_config(ibmq_api_token=ibm_token, instance=f"{hub}/{group}/{project}") - -.. currentmodule:: pytket.extensions.qiskit.backends.config - -.. autosummary:: - :nosignatures: - - QiskitConfig - set_ibmq_config - -Converting circuits between pytket and qiskit -============================================= - -Users may wish to port quantum circuits between pytket and qiskit. This allows the features of both libraries to be used. -For instance those familiar with qiskit may wish to convert their circuits to pytket and use the available compilation passes to optimise circuits. - -.. currentmodule:: pytket.extensions.qiskit - - -.. autosummary:: - :nosignatures: - - qiskit_to_tk - tk_to_qiskit - - -Default Compilation -=================== - -Every :py:class:`~pytket.backends.backend.Backend` in pytket has its own :py:meth:`~pytket.backends.Backend.default_compilation_pass` method. This method applies a sequence of optimisations to a circuit depending on the value of an ``optimisation_level`` parameter. This default compilation will ensure that the circuit meets all the constraints required to run on the :py:class:`~pytket.backends.backend.Backend`. The passes applied by different levels of optimisation are specified in the table below. - -.. list-table:: **Default compilation pass for the IBMQBackend and IBMQEmulatorBackend** - :widths: 25 25 25 - :header-rows: 1 - - * - optimisation_level = 0 - - optimisation_level = 1 - - optimisation_level = 2 [1] - * - `DecomposeBoxes `_ - - `DecomposeBoxes `_ - - `DecomposeBoxes `_ - * - `AutoRebase `_ [2] - - `SynthesiseTket `_ - - `FullPeepholeOptimise `_ - * - `CXMappingPass `_ [3] - - `CXMappingPass `_ [3] - - `CXMappingPass `_ [3] - * - `NaivePlacementPass `_ - - `NaivePlacementPass `_ - - `NaivePlacementPass `_ - * - `AutoRebase `_ [2] - - `SynthesiseTket `_ - - `KAKDecomposition(allow_swaps=False) `_ - * - `RemoveRedundancies `_ - - `AutoRebase `_ [2] - - `CliffordSimp(allow_swaps=False) `_ - * - - - `RemoveRedundancies `_ - - `SynthesiseTket `_ - * - - - - - `AutoRebase `_ [2] - * - - - - - `RemoveRedundancies `_ - -* [1] If no value is specified then ``optimisation_level`` defaults to a value of 2. -* [2] :py:class:`~pytket._tket.passes.AutoRebase` is a conversion to the gateset supported by the backend. For IBM quantum devices and emulators the supported gateset is either :math:`\{X, SX, Rz, CX\}`, :math:`\{X, SX, Rz, ECR\}`, or :math:`\{X, SX, Rz, CZ\}`. The more idealised Aer simulators have a much broader range of supported gates. -* [3] Here :py:class:`~pytket._tket.passes.CXMappingPass` maps program qubits to the architecture using a :py:class:`~pytket._tket.placement.NoiseAwarePlacement` - - -**Note:** The :py:meth:`~AerBackend.default_compilation_pass` for :py:class:`AerBackend` is the same as above. - - -Noise Modelling -=============== - -.. currentmodule:: pytket.extensions.qiskit.backends.crosstalk_model - -.. autosummary:: - :nosignatures: - - CrosstalkParams - - -Using TKET directly on qiskit circuits -====================================== -.. currentmodule:: pytket.extensions.qiskit - -For usage of :py:class:`~tket_backend.TketBackend` see the `qiskit integration notebook example `_. - -.. autosummary:: - :nosignatures: - - ~tket_backend.TketBackend - ~tket_pass.TketPass - ~tket_pass.TketAutoPass - ~tket_job.TketJob - - - -.. toctree:: - api.rst - changelog.rst - -.. toctree:: - :caption: Useful links - - Issue tracker - PyPi diff --git a/docs/pytket-docs-theming b/docs/pytket-docs-theming index cfbe34c4..45cc4e49 160000 --- a/docs/pytket-docs-theming +++ b/docs/pytket-docs-theming @@ -1 +1 @@ -Subproject commit cfbe34c48f88c56085b8ef65f640d0108b8a9fa6 +Subproject commit 45cc4e49f473905984b99077e8739fe18e69595e diff --git a/pytket/extensions/qiskit/backends/ibm.py b/pytket/extensions/qiskit/backends/ibm.py index c21df162..fa48f400 100644 --- a/pytket/extensions/qiskit/backends/ibm.py +++ b/pytket/extensions/qiskit/backends/ibm.py @@ -397,7 +397,7 @@ def default_compilation_pass_offline( passlist = [DecomposeBoxes()] # If you make changes to the default_compilation_pass, # then please update this page accordingly - # https://tket.quantinuum.com/extensions/pytket-qiskit/index.html#default-compilation + # https://docs.quantinuum.com/tket/extensions/pytket-qiskit/index.html#default-compilation # Edit this docs source file -> pytket-qiskit/docs/intro.txt if optimisation_level == 0: if supports_rz: diff --git a/pytket/extensions/qiskit/tket_backend.py b/pytket/extensions/qiskit/tket_backend.py index a9b46302..1c749e9d 100644 --- a/pytket/extensions/qiskit/tket_backend.py +++ b/pytket/extensions/qiskit/tket_backend.py @@ -69,9 +69,9 @@ class TketBackend(BackendV2): :py:class:`qiskit.aqua.QuantumInstance`, providing a custom :py:class:`qiskit.transpiler.PassManager` with a :py:class:`qiskit.transpiler.passes.Unroller`. For examples, see the `user manual - `_ or the `Qiskit integration example `_. + `_ or the `Qiskit integration example `_. """ def __init__(self, backend: Backend, comp_pass: Optional[BasePass] = None): diff --git a/setup.py b/setup.py index 949376c0..33a8048b 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ author_email="tket-support@quantinuum.com", python_requires=">=3.10", project_urls={ - "Documentation": "https://tket.quantinuum.com/extensions/pytket-qiskit/index.html", + "Documentation": "https://docs.quantinuum.com/tket/extensions/pytket-qiskit/index.html", "Source": "https://github.com/CQCL/pytket-qiskit", "Tracker": "https://github.com/CQCL/pytket-qiskit/issues", },