Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transpiler integration refactor and qiskit v1.2 support #116

Open
wants to merge 34 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
675533e
Minimal changes to support qiskit v1.2 - layout preservation of trans…
Sep 5, 2024
9fb91ee
Update version
Sep 5, 2024
1dd4b29
removed unused import
Sep 5, 2024
d481364
Merge https://github.com/iqm-finland/qiskit-on-iqm into qiskit-v1.2
Nov 1, 2024
a22d8db
typo
Nov 1, 2024
5ece8fa
pylint
Nov 1, 2024
5ab12d8
Fixed DQA integration. Transpiler still broken
Nov 1, 2024
6be92bb
Merge https://github.com/iqm-finland/qiskit-on-iqm into qiskit-v1.2
Nov 1, 2024
422eb93
formatting
Nov 1, 2024
1460a20
Buggy Implementation of the foundation of the transpiler integration.
Nov 1, 2024
5bb86c0
Native qiskit tranpsiler integration.
Nov 28, 2024
a0ad923
Qiskit transpiler refactor
Dec 10, 2024
6955de6
Merge https://github.com/iqm-finland/qiskit-on-iqm into qiskit-v1.2
Dec 10, 2024
eb275f2
change the fidelity number to error
Oct 30, 2024
92a5641
Deneb fix changelog update
Dec 10, 2024
99b8ed7
Updated user guide with new transpiler information
Dec 10, 2024
33296c1
Updated documentation
Dec 10, 2024
547f0ea
Removed Brittish spelling
Dec 10, 2024
b5e9e04
fixed docs
Dec 10, 2024
ea4d1bc
Fixes.
Dec 11, 2024
0f5cbe5
Changes to reflect supported CZ directions as calibrated in the hardware
Dec 12, 2024
82fc6a3
Update changelog
Dec 12, 2024
5fd22e3
fix typo
Dec 12, 2024
c56d86c
fix pylint
Dec 12, 2024
4206c1a
Delete accidental files
Dec 12, 2024
75ef5e3
fix new error message test
Dec 12, 2024
f333454
Update doc string
Dec 12, 2024
836c26f
Drop qiskit 0.45 support
Dec 12, 2024
8fadfca
Update iqmtarget docstring
Dec 12, 2024
bf46609
Removed the measure gate as a special case gate when converting a sqa…
Dec 12, 2024
be65521
Added a section of transpiler plugins
Dec 12, 2024
177fb05
Added directional CZs
Dec 12, 2024
e2e52a4
Merge branch 'directionalCZs' of https://github.com/iqm-finland/qiski…
Dec 12, 2024
0d4add2
Removed flaky duplicated assert
Dec 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,29 @@
Changelog
=========

Version 16.0
=============

* Fixed Deneb Readout errors to closer resemble reality. Fidelities were reported as errors. `#125 <https://github.com/iqm-finland/iqm-client/pull/125>`_
Aerylia marked this conversation as resolved.
Show resolved Hide resolved
* Refactored :meth:`IQMBackend.create_run_request` to improve user experience when using IQM specific run options.
* Updated the documentation for using additional run options with IQM backends.
* :meth:`IQMBackendBase.qubit_name_to_index` and :meth:`IQMBackendBase.index_to_qubit_name` now raises an error when using an invalid qubit name or index, rather than returning None.
* Introduction of `IQMBackendBase.physical_target` and `IQMBackendBase.fake_target` to represent the physical quantum architectures and a Qiskit-compatible version, respectively.
* Added support for ``qiskit == 1.2`` and ``qiskit-aer == 1.5``.
* Moved the circuit serialization logic from :class:`IQMProvider` to :mod:`iqm.qiskit_iqm.qiskit_to_iqm`.
* Refactoring of the Qiskit transpiler:
* The Qiskit transpiler now automatically uses the :class:`IQMOptimizeSingleQubitGates` pass to optimize single-qubit gates if the `optimization_level >= 0`.
* You can now use the native Qiskit :meth:`transpile` method to transpile a circuit to the IQM Deneb backend as long as your circuit does not contain any resonators.
* There are many new transpiler plugins available that you can use as the `scheduling_method` argument in Qiskit's :meth:`transpile` method. You can find them in following the `Qiskit documentation <https://docs.quantum.ibm.com/guides/transpiler-plugins>`_.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only scheduling_method? What about the other *_method args?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The plugins are only registered as scheduling_method because they make assumptions about how much the circuit has been transpiled already. You can still combine them with the other transpiler args.
You could make our own optimization plugin or routing plugin, but that would be exposed to the user as a string making them overwrite the existing transpiler and making it impossible to augment it with an existing pass. So they would need to defined with an existing optimization/routing pass and we need a new class for each or combination of passes we want to support. That's not maintainable.

* If your circuit contains resonators, and optionally :class:`MoveGate` operations, you can use the :meth:`transpile_to_IQM` method to transpile your circuit to the IQM Deneb backend.
* :meth:`transpile_to_IQM` can now restrict itself to use a restricted set of qubits by specifying the `restrict_to_qubits` argument. You will need to additionally provide a qubit mapping to the :meth:`backend.run` method to ensure that the correct qubits are used.
* Bugfix where the :meth:`transpile_to_IQM` did not retain the circuit layout after transpiling.
* Deprecated features:
* :meth:`optimize_single_qubit_gates` has been deprecated in favor of using the new transpiler plugins or :meth:`transpile_to_IQM`. Additionally, this is now incorporated into the Qiskit transpiler as documented above.
* In :meth:`IQMBackend.create_run_request`, and as a result in :meth:`IQMBackend.run`, the `max_circuit_duration_over_t2` and `heralding_mode` options have been deprecated in favor of using the `CircuitCompilationOptions` class from :mod:`iqm-client`.
* The :class:`IQMBackend` no longer uses Qiskit's `options` attribute to give run options in favor of using the arguments of the :meth:`IQMBackend.run` method directly.


Version 15.5
============

Expand Down
200 changes: 118 additions & 82 deletions docs/user_guide.rst

Large diffs are not rendered by default.

22 changes: 20 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ classifiers = [
requires-python = ">=3.9, <3.12"
dependencies = [
"numpy",
"qiskit >= 0.45, < 1.2",
"qiskit-aer >= 0.13.1, < 0.15",
"qiskit >= 0.45, < 1.3",
"qiskit-aer >= 0.13.1, < 0.16",
"iqm-client >= 20.0, < 21.0"
]

Expand Down Expand Up @@ -158,3 +158,21 @@ ignore-imports = true

[tool.pylint.string]
check-quote-consistency = true

# Expose the different custom transpiler passes to the Qiskit transpiler using their plugin system.
[project.entry-points."qiskit.transpiler.scheduling"]
move_routing = "iqm.qiskit_iqm:MoveGateRoutingPlugin"
move_routing_keep = "iqm.qiskit_iqm:MoveGateRoutingKeepExistingMovesPlugin"
move_routing_remove = "iqm.qiskit_iqm:MoveGateRoutingRemoveExistingMovesPlugin"
move_routing_trust = "iqm.qiskit_iqm:MoveGateRoutingTrustExistingMovesPlugin"
only_move_routing = "iqm.qiskit_iqm:MoveGateRoutingOnlyPlugin"
only_move_routing_keep = "iqm.qiskit_iqm:MoveGateRoutingOnlyKeepExistingMovesPlugin"
only_move_routing_remove = "iqm.qiskit_iqm:MoveGateRoutingOnlyRemoveExistingMovesPlugin"
only_move_routing_trust = "iqm.qiskit_iqm:MoveGateRoutingOnlyTrustExistingMovesPlugin"
move_routing_exact_global_phase = "iqm.qiskit_iqm:MoveGateRoutingWithExactRzPlugin"
move_routing_Rz_optimization_ignores_barriers = "iqm.qiskit_iqm:MoveGateRoutingWithRzOptimizationIgnoreBarriersPlugin"
only_Rz_optimization = "iqm.qiskit_iqm:OnlyRzOptimizationPlugin"
only_Rz_optimization_exact_global_phase = "iqm.qiskit_iqm:OnlyRzOptimizationExactPlugin"
only_Rz_optimization_ignore_barriers = "iqm.qiskit_iqm:OnlyRzOptimizationIgnoreBarriersPlugin"
iqm_default_scheduling = "iqm.qiskit_iqm:IQMDefaultSchedulingPlugin"

1 change: 1 addition & 0 deletions src/iqm/qiskit_iqm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from iqm.qiskit_iqm.iqm_provider import IQMBackend, IQMProvider, __version__
from iqm.qiskit_iqm.iqm_transpilation import IQMOptimizeSingleQubitGates, optimize_single_qubit_gates
from iqm.qiskit_iqm.move_gate import MoveGate
from iqm.qiskit_iqm.transpiler_plugins import *

if qiskit_version < "1.0.0":
warn(
Expand Down
12 changes: 6 additions & 6 deletions src/iqm/qiskit_iqm/fake_backends/fake_deneb.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,12 @@ def IQMFakeDeneb() -> IQMFakeBackend:
two_qubit_gate_durations={"cz": 120.0, "move": 96.0},
readout_errors={
"COMP_R": {"0": 0.0, "1": 0.0},
"QB1": {"0": 0.977, "1": 0.977},
"QB2": {"0": 0.977, "1": 0.977},
"QB3": {"0": 0.977, "1": 0.977},
"QB4": {"0": 0.977, "1": 0.977},
"QB5": {"0": 0.977, "1": 0.977},
"QB6": {"0": 0.977, "1": 0.977},
"QB1": {"0": 0.02, "1": 0.02},
"QB2": {"0": 0.02, "1": 0.02},
"QB3": {"0": 0.02, "1": 0.02},
"QB4": {"0": 0.02, "1": 0.02},
"QB5": {"0": 0.02, "1": 0.02},
"QB6": {"0": 0.02, "1": 0.02},
},
name="sample-chip",
)
Expand Down
61 changes: 11 additions & 50 deletions src/iqm/qiskit_iqm/fake_backends/iqm_fake_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,19 @@

from qiskit import QuantumCircuit
from qiskit.providers import JobV1, Options
from qiskit.transpiler import TransformationPass
from qiskit_aer import AerSimulator
from qiskit_aer.noise import NoiseModel
from qiskit_aer.noise.errors import depolarizing_error, thermal_relaxation_error

from iqm.iqm_client import DynamicQuantumArchitecture, QuantumArchitectureSpecification
from iqm.qiskit_iqm.iqm_backend import IQM_TO_QISKIT_GATE_NAME, IQMBackendBase
from iqm.qiskit_iqm.iqm_circuit import IQMCircuit
from iqm.qiskit_iqm.iqm_circuit_validation import validate_circuit
from iqm.qiskit_iqm.iqm_transpilation import IQMReplaceGateWithUnitaryPass
from iqm.qiskit_iqm.move_gate import MOVE_GATE_UNITARY

GATE_TO_UNITARY = {
"move": MOVE_GATE_UNITARY,
}


# pylint: disable=too-many-instance-attributes
Expand Down Expand Up @@ -281,9 +286,7 @@ def _default_options(cls) -> Options:
def max_circuits(self) -> Optional[int]:
return None

def run(
self, run_input: Union[QuantumCircuit, list[QuantumCircuit], IQMCircuit, list[IQMCircuit]], **options
) -> JobV1:
def run(self, run_input: Union[QuantumCircuit, list[QuantumCircuit]], **options) -> JobV1:
"""
Run `run_input` on the fake backend using a simulator.

Expand All @@ -302,64 +305,22 @@ def run(
Raises:
ValueError: If empty list of circuits is provided.
"""
circuits_aux = [run_input] if isinstance(run_input, (QuantumCircuit, IQMCircuit)) else run_input
circuits_aux = [run_input] if isinstance(run_input, (QuantumCircuit)) else run_input
Aerylia marked this conversation as resolved.
Show resolved Hide resolved

if len(circuits_aux) == 0:
raise ValueError("Empty list of circuits submitted for execution.")

this = self

class check_move_validity(TransformationPass):
"""Checks that the placement of move gates is valid in the circuit."""

def run(self, dag):
qubits_involved_in_last_move = None # Store which qubit was last used for MOVE IN
for node in dag.op_nodes():
if node.op.name not in this.noise_model.basis_gates + ["id", "barrier", "measure"]:
raise ValueError("Operation '" + node.op.name + "' is not supported by the backend.")
if qubits_involved_in_last_move is not None:
# Verify that no single qubit gate is performed on the qubit between MOVE IN and MOVE OUT
if (
node.op.name not in ["move", "barrier", "measure"]
and len(node.qargs) == 1
and node.qargs[0] == qubits_involved_in_last_move[0]
):
raise ValueError(
f"Operations to qubits {node.qargs[0]} while their states are moved to a resonator."
)
if node.op.name == "move":
if qubits_involved_in_last_move is None:
# MOVE IN was performed
qubits_involved_in_last_move = node.qargs
elif qubits_involved_in_last_move != node.qargs:
raise ValueError(
f"Cannot apply MOVE on {node.qargs[0]} because COMP_R already holds the state of "
+ f"{qubits_involved_in_last_move[0]}."
)
else:
# MOVE OUT was performed
qubits_involved_in_last_move = None

if qubits_involved_in_last_move is not None:
raise ValueError(
"The following resonators are still holding qubit states at the end of the circuit: "
+ f"{qubits_involved_in_last_move[0]}."
)

return dag

circuits = []

for circ in circuits_aux:
circ_to_add = circ

if "move" in self.noise_model.basis_gates:
check_move_validity()(circ)
validate_circuit(circ_to_add, self)

for iqm_gate in [
g for g in self.noise_model.basis_gates if g not in list(IQM_TO_QISKIT_GATE_NAME.values())
]:
circ_to_add = circ.decompose(gates_to_decompose=iqm_gate)
circ_to_add = IQMReplaceGateWithUnitaryPass(iqm_gate, GATE_TO_UNITARY[iqm_gate])(circ_to_add)
circuits.append(circ_to_add)

shots = options.get("shots", self.options.shots)
Expand Down
Loading
Loading