Skip to content

🎨 Update of IonQ and Rigetti Devices #570

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

Merged
merged 27 commits into from
May 25, 2025
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8f97eea
🎨 WIP for IonQ Device handling
nquetschlich May 22, 2025
ea45fa2
Merge branch 'main' into update_ionq_devices
nquetschlich May 22, 2025
33a23f5
handled linter warnings
nquetschlich May 22, 2025
60575ae
♻️ refactored module structrue
nquetschlich May 23, 2025
a0799c1
♻️ refactored ionq handling
nquetschlich May 23, 2025
9831391
♻️ refactored gateset handling
nquetschlich May 23, 2025
1031198
♻️ refactored the IonQ gate definitions
nquetschlich May 23, 2025
c44b965
✨ introduced the new IonQ gates also to the native gates level
nquetschlich May 23, 2025
0a2e568
💡 added copyrights
nquetschlich May 23, 2025
64b96d3
🎨 improved ionq gateset handling
nquetschlich May 23, 2025
8d54a14
🎨 simplified gate definition
nquetschlich May 23, 2025
3ac576f
update Rigetti Device and Gateset accordingly to IonQ one
nquetschlich May 23, 2025
e6279f7
✅ added test to increase coverage
nquetschlich May 23, 2025
58b8de4
🚨
nquetschlich May 23, 2025
a27e24f
🎨 renamed custom rigetti gates
nquetschlich May 23, 2025
bc53846
🔥 removed unnecessary array definition fur custom gates
nquetschlich May 23, 2025
d9e8084
🎨 improved equivalence handling
nquetschlich May 23, 2025
b13e824
🎨 use relative imports instead of absolute ones
nquetschlich May 23, 2025
db651b3
🎨 renamed custom rigetti gates
nquetschlich May 23, 2025
e300c9b
🎨 adjusted the naming scheme of devices
nquetschlich May 23, 2025
f1666f4
🎨 removed tests that used an internal method
nquetschlich May 23, 2025
2eda174
🔥 removed unnecessary equivalence
nquetschlich May 23, 2025
371a77e
✨ added a rx decomposition
nquetschlich May 23, 2025
4c5666e
✨ improved the equivalence adding logic
nquetschlich May 23, 2025
9645895
✨ removed comment
nquetschlich May 23, 2025
f2b5fee
Merge branch 'main' into update_ionq_devices
nquetschlich May 24, 2025
4b1f0ee
Merge branch 'main' into update_ionq_devices
nquetschlich May 25, 2025
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
8 changes: 5 additions & 3 deletions src/mqt/bench/targets/devices/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from functools import cache
from typing import TYPE_CHECKING

from ..gatesets.ionq import add_ionq_equivalences
from .ibm import get_ibm_target
from .ionq import get_ionq_target
from .iqm import get_iqm_target
Expand All @@ -24,6 +25,7 @@


__all__ = [
"add_ionq_equivalences",
"get_available_devices",
"get_device_by_name",
]
Expand All @@ -38,13 +40,13 @@ def get_available_devices() -> list[Target]:
get_ibm_target("ibm_eagle_127"),
get_ibm_target("ibm_heron_133"),
get_ibm_target("ibm_heron_156"),
get_ionq_target("ionq_harmony"),
get_ionq_target("ionq_aria1"),
get_ionq_target("ionq_forte_1"),
get_ionq_target("ionq_aria_1"),
Copy link
Member

Choose a reason for hiding this comment

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

I believe the naming of these should be adjusted so that the last number indicates the number of qubits similar to IBM and IQM. The 1 here does not add any value

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

These are the specific device names and there is also an Aria 2. Therefore, I suggest to keep the name, but this is independent of the qubit number at the end - if you prefer that, I am happy to add it to all devices.

Copy link
Member

Choose a reason for hiding this comment

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

But aria 2 still uses the same layout and the same gateset if I am not mistaken.
Given how the device data is averaged anyway, I do not think there is too much value to this number.

If the gateset should really be different between these, then I would actually add the number to the gateset name and still append an underscore with the number of qubits for the device here.

Same holds for the Rigetti device.

get_iqm_target("iqm_crystal_5"),
get_iqm_target("iqm_crystal_20"),
get_iqm_target("iqm_crystal_54"),
get_quantinuum_target("quantinuum_h2"),
get_rigetti_target("rigetti_aspen_m3"),
get_rigetti_target("rigetti_ankaa_3"),
]


Expand Down
2 changes: 1 addition & 1 deletion src/mqt/bench/targets/devices/ibm.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

from qiskit.providers.fake_provider import GenericBackendV2

from ..gatesets import get_ibm_eagle_gateset, get_ibm_falcon_gateset, get_ibm_heron_gateset
from mqt.bench.targets.gatesets.ibm import get_ibm_eagle_gateset, get_ibm_falcon_gateset, get_ibm_heron_gateset
Copy link
Member

Choose a reason for hiding this comment

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

This change is unrelated to the PR and I actually would not make that change.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Adjusted it accordingly.


logger = logging.getLogger(__name__)

Expand Down
70 changes: 42 additions & 28 deletions src/mqt/bench/targets/devices/ionq.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,54 +11,60 @@
from __future__ import annotations

from qiskit.circuit import Parameter
from qiskit.circuit.library import Measure, RXGate, RXXGate, RYGate, RZGate
from qiskit.circuit.library import Measure, RZGate
from qiskit.transpiler import InstructionProperties, Target

from mqt.bench.targets.gatesets.ionq import GPI2Gate, GPIGate, MSGate, ZZGate, add_ionq_equivalences
Copy link
Member

Choose a reason for hiding this comment

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

I would have a rather strong preference for relative imports. Tends to work better with editable installs and IDEs.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Adjusted it accordingly.



def get_ionq_target(device_name: str) -> Target:
"""Get the target device for a given IonQ device name."""
if device_name == "ionq_aria1":
return get_ionq_aria1_target()
if device_name == "ionq_harmony":
return get_ionq_harmony_target()
add_ionq_equivalences()
Copy link
Member

Choose a reason for hiding this comment

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

I am not yet quite sure this is the best place to put this. As there are multiple IonQ devices, this will inevitably get called multiple times.
I could imagine that this literally duplicates all the rules, which doesn't sound good.

I think it would be good to ensure that this is only called once. And preferably, it would only be called when the target is used, not when it's constructed. We only want to add these right before the transpile call when the target is used.

I'd also like to understand a little bit more about the lifecycle of the SessionEquivalenceLibrary. How long does it persist? Where does it enter the equation in the transpile call? Would moving to the StagedPassManager allow us to use a custom EL?

if device_name == "ionq_aria_1":
return get_ionq_aria_1_target()
if device_name == "ionq_forte_1":
return get_ionq_forte_1_target()
msg = f"Unknown IonQ device: '{device_name}'"
raise ValueError(msg)


def get_ionq_aria1_target() -> Target:
"""Get the target device for IonQ Aria1."""
def get_ionq_aria_1_target() -> Target:
"""Get the target device for IonQ Aria 1."""
num_qubits = 25
return _build_ionq_target(
num_qubits=num_qubits,
description="ionq_aria1",
description="ionq_aria_1",
entangling_gate="MS",
oneq_duration=135e-6,
twoq_duration=600e-6,
readout_duration=300e-6,
oneq_fidelity=0.9829,
twoq_fidelity=0.996,
spam_fidelity=0.9993,
oneq_fidelity=0.9998,
twoq_fidelity=0.98720,
spam_fidelity=0.99370,
)


def get_ionq_harmony_target() -> Target:
"""Get the target device for IonQ Harmony."""
num_qubits = 11
def get_ionq_forte_1_target() -> Target:
"""Get the target device for IonQ Forte 1."""
num_qubits = 36
return _build_ionq_target(
num_qubits=num_qubits,
description="ionq_harmony",
oneq_duration=10e-6,
twoq_duration=200e-6,
readout_duration=130e-6,
oneq_fidelity=0.9985,
twoq_fidelity=0.9614,
spam_fidelity=0.99752,
description="ionq_forte_1",
entangling_gate="ZZ",
oneq_duration=130e-6,
twoq_duration=970e-6,
readout_duration=150e-6,
oneq_fidelity=0.9998,
twoq_fidelity=0.9932,
spam_fidelity=0.9959,
)


def _build_ionq_target(
*,
num_qubits: int,
description: str,
entangling_gate: str,
oneq_duration: float,
twoq_duration: float,
readout_duration: float,
Expand All @@ -71,21 +77,19 @@ def _build_ionq_target(

theta = Parameter("theta")
phi = Parameter("phi")
lam = Parameter("lambda")
alpha = Parameter("alpha")

# === Add single-qubit gates ===
singleq_props = {
(q,): InstructionProperties(duration=oneq_duration, error=1 - oneq_fidelity) for q in range(num_qubits)
}
rz_props = {(q,): InstructionProperties(duration=0.0, error=0.0) for q in range(num_qubits)}
rz_props = {(q,): InstructionProperties(duration=0, error=0) for q in range(num_qubits)}
measure_props = {
(q,): InstructionProperties(duration=readout_duration, error=1 - spam_fidelity) for q in range(num_qubits)
}

target.add_instruction(RXGate(theta), singleq_props)
target.add_instruction(RYGate(phi), singleq_props)
target.add_instruction(RZGate(lam), rz_props)
target.add_instruction(RZGate(theta), rz_props)
target.add_instruction(GPIGate(theta), singleq_props)
target.add_instruction(GPI2Gate(phi), singleq_props)
target.add_instruction(Measure(), measure_props)

# === Add two-qubit gates ===
Expand All @@ -94,5 +98,15 @@ def _build_ionq_target(
(q1, q2): InstructionProperties(duration=twoq_duration, error=1 - twoq_fidelity) for q1, q2 in connectivity
}

target.add_instruction(RXXGate(alpha), twoq_props)
if entangling_gate == "MS":
alpha = Parameter("alpha")
beta = Parameter("beta")
gamma = Parameter("gamma")
target.add_instruction(MSGate(alpha, beta, gamma), twoq_props)
elif entangling_gate == "ZZ":
alpha = Parameter("alpha")
target.add_instruction(ZZGate(alpha), twoq_props)
else:
msg = f"Unknown entangling gate: '{entangling_gate}'."
raise ValueError(msg)
return target
Loading
Loading