Skip to content

Commit

Permalink
Merge pull request #162 from CQCL/release/1.18august
Browse files Browse the repository at this point in the history
Release/1.18august
  • Loading branch information
cqc-melf authored Aug 15, 2023
2 parents ca50678 + 16592c6 commit c3e3339
Show file tree
Hide file tree
Showing 10 changed files with 75 additions and 40 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ jobs:
mkdir extensions
./build-docs -d ${GITHUB_WORKSPACE}/.github/workflows/docs/extensions/api
- name: Upload docs as artefact
uses: actions/upload-pages-artifact@v1
uses: actions/upload-pages-artifact@v2
with:
path: .github/workflows/docs/extensions

Expand Down
2 changes: 1 addition & 1 deletion _metadata.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
__extension_version__ = "0.42.0"
__extension_version__ = "0.43.0"
__extension_name__ = "pytket-qiskit"
10 changes: 10 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
Changelog
~~~~~~~~~

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)
--------------------

Expand Down
45 changes: 33 additions & 12 deletions pytket/extensions/qiskit/backends/aer.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
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 pytket.architecture import Architecture # type: ignore
from pytket.architecture import Architecture, FullyConnected # type: ignore
from pytket.backends import Backend, CircuitNotRunError, CircuitStatus, ResultHandle
from pytket.backends.backendinfo import BackendInfo
from pytket.backends.backendresult import BackendResult
Expand Down Expand Up @@ -109,6 +109,7 @@ class _AerBaseBackend(Backend):
_memory: bool
_required_predicates: List[Predicate]
_noise_model: Optional[NoiseModel] = None
_has_arch: bool = False

@property
def required_predicates(self) -> List[Predicate]:
Expand Down Expand Up @@ -204,7 +205,11 @@ def default_compilation_pass(
See documentation for :py:meth:`IBMQBackend.default_compilation_pass`.
"""
arch = self._backend_info.architecture
if arch.coupling and self._backend_info.get_misc("characterisation"):
if (
self._has_arch
and arch.coupling
and self._backend_info.get_misc("characterisation")
):
return self._arch_dependent_default_compilation_pass(
arch, optimisation_level, placement_options=placement_options
)
Expand Down Expand Up @@ -431,15 +436,17 @@ def __init__(
self,
noise_model: Optional[NoiseModel] = None,
simulation_method: str = "automatic",
n_qubits: int = 40,
):
"""Backend for running simulations on the Qiskit Aer QASM simulator.
:param noise_model: Noise model to apply during simulation. Defaults to None.
:type noise_model: Optional[NoiseModel], optional
:param simulation_method: Simulation method, see
https://qiskit.org/documentation/stubs/qiskit.providers.aer.AerSimulator.html
for available values. Defaults to "automatic".
https://qiskit.org/documentation/stubs/qiskit.providers.aer.AerSimulator.html
for available values. Defaults to "automatic".
:type simulation_method: str
:param n_qubits: The maximum number of qubits supported by the backend.
"""
super().__init__()
self._qiskit_backend: "QiskitAerBackend" = Aer.get_backend(
Expand All @@ -453,12 +460,17 @@ def __init__(
characterisation = _get_characterisation_of_noise_model(
self._noise_model, gate_set
)
self._has_arch = (
characterisation.architecture and characterisation.architecture.nodes
)

self._backend_info = BackendInfo(
name=type(self).__name__,
device_name=self._qiskit_backend_name,
version=__extension_version__,
architecture=characterisation.architecture,
architecture=characterisation.architecture
if self._has_arch
else FullyConnected(n_qubits),
gate_set=gate_set,
supports_midcircuit_measurement=True, # is this correct?
supports_fast_feedforward=True,
Expand All @@ -475,7 +487,7 @@ def __init__(
NoSymbolsPredicate(),
GateSetPredicate(self._backend_info.gate_set),
]
if characterisation.architecture.coupling:
if self._has_arch:
# architecture is non-trivial
self._required_predicates.append(
ConnectivityPredicate(characterisation.architecture)
Expand All @@ -493,8 +505,14 @@ class AerStateBackend(_AerBaseBackend):

_qiskit_backend_name = "aer_simulator_statevector"

def __init__(self) -> None:
"""Backend for running simulations on the Qiskit Aer Statevector simulator."""
def __init__(
self,
n_qubits: int = 40,
) -> None:
"""Backend for running simulations on the Qiskit Aer Statevector simulator.
:param n_qubits: The maximum number of qubits supported by the backend.
"""
super().__init__()
self._qiskit_backend: "QiskitAerBackend" = Aer.get_backend(
self._qiskit_backend_name
Expand All @@ -503,7 +521,7 @@ def __init__(self) -> None:
name=type(self).__name__,
device_name=self._qiskit_backend_name,
version=__extension_version__,
architecture=Architecture([]),
architecture=FullyConnected(n_qubits),
gate_set=_tket_gate_set_from_qiskit_backend(self._qiskit_backend),
supports_midcircuit_measurement=True, # is this correct?
misc={"characterisation": None},
Expand All @@ -524,8 +542,11 @@ class AerUnitaryBackend(_AerBaseBackend):

_qiskit_backend_name = "aer_simulator_unitary"

def __init__(self) -> None:
"""Backend for running simulations on the Qiskit Aer Unitary simulator."""
def __init__(self, n_qubits: int = 40) -> None:
"""Backend for running simulations on the Qiskit Aer Unitary simulator.
:param n_qubits: The maximum number of qubits supported by the backend.
"""
super().__init__()
self._qiskit_backend: "QiskitAerBackend" = Aer.get_backend(
self._qiskit_backend_name
Expand All @@ -534,7 +555,7 @@ def __init__(self) -> None:
name=type(self).__name__,
device_name=self._qiskit_backend_name,
version=__extension_version__,
architecture=Architecture([]),
architecture=FullyConnected(n_qubits),
gate_set=_tket_gate_set_from_qiskit_backend(self._qiskit_backend),
supports_midcircuit_measurement=True, # is this correct?
misc={"characterisation": None},
Expand Down
21 changes: 1 addition & 20 deletions pytket/extensions/qiskit/backends/ibm.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,7 @@
CliffordSimp,
SimplifyInitial,
NaivePlacementPass,
RebaseCustom,
)
from pytket.passes._decompositions import _TK1_to_X_SX_Rz
from pytket.predicates import ( # type: ignore
NoMidMeasurePredicate,
NoSymbolsPredicate,
Expand Down Expand Up @@ -141,20 +139,6 @@ def _save_ibmq_auth(qiskit_config: Optional[QiskitConfig]) -> None:
QiskitRuntimeService.save_account(channel="ibm_quantum", token=token)


# Variables for defining a rebase to the {X, SX, Rz, ECR} gateset
# See https://github.com/CQCL/pytket-qiskit/issues/112
_cx_replacement_with_ecr = (
Circuit(2).X(0).SX(1).Rz(-0.5, 0).add_gate(OpType.ECR, [0, 1])
)
_tk1_replacement_function = _TK1_to_X_SX_Rz

ecr_rebase = RebaseCustom(
gateset={OpType.X, OpType.SX, OpType.Rz, OpType.ECR},
cx_replacement=_cx_replacement_with_ecr,
tk1_replacement=_tk1_replacement_function,
)


def _get_primitive_gates(gateset: Set[OpType]) -> Set[OpType]:
if gateset >= {OpType.X, OpType.SX, OpType.Rz, OpType.CX}:
return {OpType.X, OpType.SX, OpType.Rz, OpType.CX}
Expand Down Expand Up @@ -456,10 +440,7 @@ def _result_id_type(self) -> _ResultIdTuple:
return (str, int, int, str)

def rebase_pass(self) -> BasePass:
if self._primitive_gates == {OpType.X, OpType.SX, OpType.Rz, OpType.ECR}:
return ecr_rebase
else:
return auto_rebase_pass(self._primitive_gates)
return auto_rebase_pass(self._primitive_gates)

def process_circuits(
self,
Expand Down
10 changes: 9 additions & 1 deletion pytket/extensions/qiskit/qiskit_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,15 @@ def add_qiskit_data(self, data: "QuantumCircuitData") -> None:
elif type(instr) in [PauliEvolutionGate, UnitaryGate]:
pass # Special handling below
else:
optype = _known_qiskit_gate[type(instr)]
try:
optype = _known_qiskit_gate[type(instr)]
except KeyError:
raise NotImplementedError(
f"Conversion of qiskit's {instr.name} instruction is "
+ "currently unsupported by qiskit_to_tk. Consider "
+ "using QuantumCircuit.decompose() before attempting "
+ "conversion."
)
qubits = [self.qbmap[qbit] for qbit in qargs]
bits = [self.cbmap[bit] for bit in cargs]

Expand Down
3 changes: 3 additions & 0 deletions pytket/extensions/qiskit/result_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ def qiskit_experimentresult_to_backendresult(
result: ExperimentResult,
ppcirc: Optional[Circuit] = None,
) -> BackendResult:
if not result.success:
raise RuntimeError(result.status)

header = result.header
width = header.memory_slots

Expand Down
3 changes: 3 additions & 0 deletions pytket/extensions/qiskit/tket_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from qiskit.providers.backend import BackendV1 as QiskitBackend # type: ignore
from qiskit.providers.models import QasmBackendConfiguration # type: ignore
from qiskit.providers import Options # 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
Expand Down Expand Up @@ -128,6 +129,8 @@ def run(
jobinfos = []
for qc in run_input:
tk_circ = qiskit_to_tk(qc)
if isinstance(self._backend, (AerStateBackend, AerUnitaryBackend)):
tk_circ.remove_blank_wires()
circ_list.append(tk_circ)
jobinfos.append(JobInfo(qc.name, tk_circ.qubits, tk_circ.bits, n_shots))
if self._comp_pass:
Expand Down
8 changes: 4 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@
include_package_data=True,
install_requires=[
"pytket ~= 1.18",
"qiskit ~= 0.43.1",
"qiskit-ibm-runtime ~= 0.11.1",
"qiskit-aer ~= 0.12.0",
"qiskit-ibm-provider ~= 0.6.1",
"qiskit ~= 0.44.0",
"qiskit-ibm-runtime ~= 0.11.3",
"qiskit-aer ~= 0.12.2",
"qiskit-ibm-provider ~= 0.6.3",
"numpy",
],
classifiers=[
Expand Down
11 changes: 10 additions & 1 deletion tests/qiskit_convert_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from qiskit.opflow.primitive_ops import PauliSumOp # type: ignore
from qiskit.quantum_info import Pauli # type: ignore
from qiskit.transpiler import PassManager # type: ignore
from qiskit.circuit.library import RYGate, MCMT # type: ignore
from qiskit.circuit.library import RYGate, MCMT, XXPlusYYGate # type: ignore
import qiskit.circuit.library.standard_gates as qiskit_gates # type: ignore
from qiskit.circuit import Parameter # type: ignore
from qiskit_aer import Aer # type: ignore
Expand Down Expand Up @@ -1004,3 +1004,12 @@ def test_CS_and_CSdg() -> None:
qiskit_qc.append(qiskit_gates.CSdgGate(), [1, 0])
tkc = qiskit_to_tk(qiskit_qc)
assert tkc.n_gates_of_type(OpType.QControlBox) == 4


def test_failed_conversion_error() -> None:
qc = QuantumCircuit(2)
qc.append(XXPlusYYGate(0.1), [0, 1]) # add unsupported gate
with pytest.raises(
NotImplementedError, match=r"Conversion of qiskit's xx_plus_yy instruction"
):
qiskit_to_tk(qc)

0 comments on commit c3e3339

Please # to comment.