Plasma-SABRE tutorial#

This tutorial gives a practical overview of the plasma-sabre transpiler for Qrisp, including user-facing passes and the IQM execution flow.

What you will learn#

  • How to build and inspect a Qrisp circuit

  • How to compose transpilation pipelines with PassManager

  • How to use layout/routing passes:

    • plasma_layout

    • plasma_route

    • vf2pp_layout

    • manual_layout

  • How to use gate conversion passes:

    • convert_to_cz

    • convert_to_prx

  • How to run full IQM transpilation via transpile_to_iqm

  • How to submit a transpiled circuit to an IQM backend

Setup#

# Core imports
from qrisp import QuantumCircuit, Qubit, Clbit, PassManager, convert_to_cz, convert_to_prx, manual_layout

# IQM imports
from iqm.qrisp_iqm import plasma_layout, plasma_route, vf2pp_layout, transpile_to_iqm

print("Imports successful.")

Output:

WARNING:jax._src.xla_bridge:864: An NVIDIA GPU may be present on this machine, but a CUDA-enabled jaxlib is not installed. Falling back to cpu.
Imports successful.

Demo circuit#

We define a small demo circuit with various two-qubit interactions to exercise the transpiler:

def build_demo_circuit() -> QuantumCircuit:
    """Build a small demo circuit with 2-qubit interactions."""
    qc = QuantumCircuit()

    # Give logical qubits distinctive names
    for i in range(4):
        qc.add_qubit(Qubit("original_qb_" + str(i)))

    for i in range(4):
        qc.add_clbit(Clbit("c" + str(i)))

    qc.h(0)
    qc.cx(0, 1)
    qc.ry(0.7, 2)
    qc.cz(1, 2)
    qc.cx(2, 3)
    qc.s(1)
    qc.cy(0, 2)

    # Add measurements for execution workflows
    qc.measure(qc.qubits, qc.clbits)
    return qc

qc = build_demo_circuit()
print(qc)

Output:

                  ┌───┐                        ┌─┐
original_qb_0: ───┤ H ├─────■────────────■─────┤M├───
                  └───┘   ┌─┴─┐   ┌───┐  │  ┌─┐└╥┘
original_qb_1: ───────────┤ X ├─■─┤ S ├──┼──┤M├─╫────
               ┌─────────┐└───┘ │ └───┘┌─┴─┐└╥┘ ║ ┌─┐
original_qb_2: ┤ Ry(0.7) ├──────■───■──┤ Y ├─╫──╫─┤M├
               └─────────┘        ┌─┴─┐└┬─┬┘ ║  ║ └╥┘
original_qb_3: ───────────────────┤ X ├─┤M├──╫──╫──╫─
                                  └───┘ └╥┘  ║  ║  ║
           c0: ══════════════════════════╬═══╬══╩══╬═
                                         ║   ║     ║
           c1: ══════════════════════════╬═══╩═════╬═
                                         ║         ║
           c2: ══════════════════════════╬═════════╩═
                                         ║
           c3: ══════════════════════════╩═══════════

1. plasma_layout — Finding the best initial placement#

plasma_layout solves the initial placement problem: given a quantum circuit with logical qubits and a hardware topology with physical qubits connected by edges, find a mapping of logical → physical qubits that minimizes the routing cost downstream.

What it does concretely#

The pass assigns each logical qubit in the circuit to a physical qubit on the QPU. No SWAP gates are inserted; the gate sequence stays the same, only the qubit indices change. For instance, if the original circuit applies cx(0, 3) and plasma_layout decides logical qubit 0 should live on physical qubit 2 and logical qubit 3 on physical qubit 1, the output circuit will contain cx(2, 1) (and will have as many qubits as the hardware topology, not just the circuit).

Note

The resulting circuit is not necessarily executable yet: it may still need a routing pass that inserts SWAP gates.

Example topology — a “cut ring”#

Throughout this tutorial we use 6 physical qubits connected in a ring with the edge (0, 1) removed:

1 — 2 — 3 — 4 — 5 — 0

This is essentially a chain 1-2-3-4-5-0. Because one edge of the ring is missing, the layout pass actually has to work: naïvely mapping logical qubit 0 to physical qubit 0 and logical qubit 1 to physical qubit 1 would place them on opposite ends of the chain, requiring many SWAPs. plasma_layout finds a better placement.

Relevant parameters#

Parameter

Default

Description

connectivity

(required)

List of (u, v) edges describing hardware connectivity

effort

30

More effort → more random candidates and refinement iterations → better layouts but slower compile

depth_weight

0.0

-1 = optimise for gate count only, 0 = balanced, +1 = optimise for depth only

VF2++ fast path#

Before starting the stochastic search, plasma_layout first tries a VF2++ subgraph isomorphism check. If the circuit’s qubit-interaction graph is already a subgraph of the hardware topology, the circuit can be mapped with zero routing cost and VF2++ returns immediately. The stochastic search is only triggered when VF2++ fails — which is the case for our demo circuit, since the cy(0, 2) gate creates an interaction between qubits 0 and 2 that doesn’t correspond to any single edge in our cut-ring topology.

# Demonstrate plasma_layout alone: only qubit placement, no SWAPs
connectivity = [
    (1, 2), (2, 3), (3, 4), (4, 5), (5, 0)
]

pm_layout_only = PassManager()
pm_layout_only += plasma_layout(connectivity, effort=40)

original = build_demo_circuit()
laid_out = pm_layout_only.run(original)

print("Original circuit (4 qubits):")
print(original)
print(f"\nAfter plasma_layout (now {laid_out.num_qubits()} physical qubits, same gates, remapped indices):")
print(laid_out)

Output:

Original circuit (4 qubits):
                  ┌───┐                        ┌─┐
original_qb_0: ───┤ H ├─────■────────────■─────┤M├───
                  └───┘   ┌─┴─┐   ┌───┐  │  ┌─┐└╥┘
original_qb_1: ───────────┤ X ├─■─┤ S ├──┼──┤M├─╫────
               ┌─────────┐└───┘ │ └───┘┌─┴─┐└╥┘ ║ ┌─┐
original_qb_2: ┤ Ry(0.7) ├──────■───■──┤ Y ├─╫──╫─┤M├
               └─────────┘        ┌─┴─┐└┬─┬┘ ║  ║ └╥┘
original_qb_3: ───────────────────┤ X ├─┤M├──╫──╫──╫─
                                  └───┘ └╥┘  ║  ║  ║
           c0: ══════════════════════════╬═══╬══╩══╬═
                                         ║   ║     ║
           c1: ══════════════════════════╬═══╩═════╬═
                                         ║         ║
           c2: ══════════════════════════╬═════════╩═
                                         ║
           c3: ══════════════════════════╩═══════════

After plasma_layout (now 6 physical qubits, same gates, remapped indices):

 padding_qb_0: ──────────────────────────────────────

 padding_qb_1: ──────────────────────────────────────
                  ┌───┐                        ┌─┐
original_qb_0: ───┤ H ├─────■────────────■─────┤M├───
                  └───┘   ┌─┴─┐   ┌───┐  │  ┌─┐└╥┘
original_qb_1: ───────────┤ X ├─■─┤ S ├──┼──┤M├─╫────
               ┌─────────┐└───┘ │ └───┘┌─┴─┐└╥┘ ║ ┌─┐
original_qb_2: ┤ Ry(0.7) ├──────■───■──┤ Y ├─╫──╫─┤M├
               └─────────┘        ┌─┴─┐└┬─┬┘ ║  ║ └╥┘
original_qb_3: ───────────────────┤ X ├─┤M├──╫──╫──╫─
                                  └───┘ └╥┘  ║  ║  ║
           c0: ══════════════════════════╬═══╬══╩══╬═
                                         ║   ║     ║
           c1: ══════════════════════════╬═══╩═════╬═
                                         ║         ║
           c2: ══════════════════════════╬═════════╩═
                                         ║
           c3: ══════════════════════════╩═══════════

2. plasma_route — SWAP insertion#

plasma_route takes a circuit that already has a fixed layout (e.g. from plasma_layout) and inserts SWAP gates so that every 2-qubit gate acts on physically adjacent qubits.

Relevant parameters#

Parameter

Default

Description

connectivity

(required)

Hardware topology edges

effort

30

More effort → better results, slower compile

depth_weight

0.0

-1 = gate count, 0 = balanced, +1 = depth

Combined pipeline: plasma_layout → plasma_route#

In practice you always chain the two passes. Make sure depth_weight matches between them.

# Example hardware topology (6 physical qubits):
connectivity = [
    (1, 2), (2, 3), (3, 4), (4, 5), (5, 0)
]

pm = PassManager()
pm += plasma_layout(connectivity, effort=40)
pm += plasma_route(connectivity, effort=40)

routed_qc = pm.run(build_demo_circuit())
print(routed_qc)

Output:

 padding_qb_0: ─────────────────────────────────────────

 padding_qb_1: ─────────────────────────────────────────
                  ┌───┐                              ┌─┐
original_qb_0: ───┤ H ├─────■──────────────────■─────┤M├
                  └───┘   ┌─┴─┐   ┌───┐┌─┐   ┌─┴─┐┌─┐└╥┘
original_qb_1: ───────────┤ X ├─■─┤ S ├┤M├─X─┤ Y ├┤M├─╫─
               ┌─────────┐└───┘ │ └───┘└╥┘ │ └───┘└╥┘ ║
original_qb_2: ┤ Ry(0.7) ├──■───■───────╫──X───────╫──╫─
               └─────────┘┌─┴─┐┌─┐      ║          ║  ║
original_qb_3: ───────────┤ X ├┤M├──────╫──────────╫──╫─
                          └───┘└╥┘      ║          ║  ║
           c0: ═════════════════╬═══════╬══════════╬══╩═
                                ║       ║          ║
           c1: ═════════════════╬═══════╩══════════╬════
                                ║                  ║
           c2: ═════════════════╬══════════════════╩════
                                ║
           c3: ═════════════════╩═══════════════════════

3. vf2pp_layout#

vf2pp_layout attempts to embed the circuit’s interaction graph directly into the topology via VF2++ subgraph isomorphism. If successful, the circuit can run without any routing SWAPs at all.

Use this when you suspect your circuit connectivity already fits the device graph. Note: plasma_layout already tries VF2++ internally as a fast path — this standalone pass is useful when you want to only attempt VF2++. A crucial difference to calling VF2++ from plasma_layout is that vf2pp_layout will raise an Exception if there is no perfect layout. plasma_layout will simply proceed with heuristic layout selection.

When does it fail?#

Our demo circuit has the interaction cy(0, 2), i.e. logical qubits 0 and 2 talk to each other although they are not neighbours on the cut-ring chain 1-2-3-4-5-0. No relabelling can fix this because the interaction graph contains a “triangle-like” structure that doesn’t fit into a path — so VF2++ raises an error.

When does it succeed?#

A circuit whose interactions already form a subgraph of the topology will succeed. Below we show both cases.

pm_vf2 = PassManager()
pm_vf2 += vf2pp_layout(connectivity)

# --- Case 1: demo circuit (fails — interaction graph doesn't fit the chain) ---
try:
    vf2_qc = pm_vf2.run(build_demo_circuit())
    print("VF2++ layout succeeded on demo circuit:")
    print(vf2_qc)
except ValueError as err:
    print("VF2++ layout failed on demo circuit (expected — cy(0,2) is non-adjacent):")
    print(err)

# --- Case 2: a circuit with only path-like interactions (succeeds) ---
print("\n--- Circuit with path interactions ---")
path_qc = QuantumCircuit()
# Give logical qubits distinctive names
for i in range(4):
    path_qc.add_qubit(Qubit("original_qb_" + str(i)))
for i in range(4):
    path_qc.add_clbit(Clbit("c_" + str(i)))
path_qc.h(0)
path_qc.cx(0, 1)       # 0-1
path_qc.cx(1, 2)       # 1-2
path_qc.cx(2, 3)       # 2-3
path_qc.measure(path_qc.qubits, path_qc.clbits)

print("Path circuit (interactions: 0-1, 1-2, 2-3):")
print(path_qc)

vf2_path_qc = pm_vf2.run(path_qc)
print(f"VF2++ succeeded — mapped to {vf2_path_qc.num_qubits()} physical qubits, zero SWAPs needed:")
print(vf2_path_qc)

Output:

VF2++ layout failed on demo circuit (expected — cy(0,2) is non-adjacent):
VF2++ could not find a matching qubit set for the circuit. The circuit's connectivity graph is not a subgraph of the topology. Consider using 'plasma_layout' and 'plasma_route' for circuits requiring swap insertion.

--- Circuit with path interactions ---
Path circuit (interactions: 0-1, 1-2, 2-3):
               ┌───┐          ┌─┐
original_qb_0: ┤ H ├──■───────┤M├──────────────
               └───┘┌─┴─┐     └╥┘     ┌─┐
original_qb_1: ─────┤ X ├──■───╫──────┤M├──────
                    └───┘┌─┴─┐ ║      └╥┘┌─┐
original_qb_2: ──────────┤ X ├─╫───■───╫─┤M├───
                         └───┘ ║ ┌─┴─┐ ║ └╥┘┌─┐
original_qb_3: ────────────────╫─┤ X ├─╫──╫─┤M├
                               ║ └───┘ ║  ║ └╥┘
          c_0: ════════════════╩═══════╬══╬══╬═
                                       ║  ║  ║
          c_1: ════════════════════════╩══╬══╬═
                                          ║  ║
          c_2: ═══════════════════════════╩══╬═
                                             ║
          c_3: ══════════════════════════════╩═

VF2++ succeeded — mapped to 6 physical qubits, zero SWAPs needed:

 padding_qb_0: ────────────────────────────────
               ┌───┐          ┌─┐
original_qb_0: ┤ H ├──■───────┤M├──────────────
               └───┘┌─┴─┐     └╥┘     ┌─┐
original_qb_1: ─────┤ X ├──■───╫──────┤M├──────
                    └───┘┌─┴─┐ ║      └╥┘┌─┐
original_qb_2: ──────────┤ X ├─╫───■───╫─┤M├───
                         └───┘ ║ ┌─┴─┐ ║ └╥┘┌─┐
original_qb_3: ────────────────╫─┤ X ├─╫──╫─┤M├
                               ║ └───┘ ║  ║ └╥┘
 padding_qb_1: ────────────────╫───────╫──╫──╫─
                               ║       ║  ║  ║
          c_0: ════════════════╩═══════╬══╬══╬═
                                       ║  ║  ║
          c_1: ════════════════════════╩══╬══╬═
                                          ║  ║
          c_2: ═══════════════════════════╩══╬═
                                             ║
          c_3: ══════════════════════════════╩═

4. manual_layout#

manual_layout lets you choose physical qubits explicitly.

  • Input: qubit_mapping, where logical qubit i maps to physical qubit_mapping[i]

  • Mapping must be the same length as circuit qubits, with unique non-negative indices

This is useful when you want deterministic placement (e.g. due to calibration data).

# Map 4 logical qubits -> physical qubits [1, 2, 4, 5]
manual_map = [1, 2, 4, 5]

pm_manual = PassManager()
pm_manual += manual_layout(manual_map)

manual_qc = pm_manual.run(build_demo_circuit())
print(manual_qc)

Output:

 padding_qb_0: ──────────────────────────────────────
                  ┌───┐                        ┌─┐
original_qb_0: ───┤ H ├─────■────────────■─────┤M├───
                  └───┘   ┌─┴─┐   ┌───┐  │  ┌─┐└╥┘
original_qb_1: ───────────┤ X ├─■─┤ S ├──┼──┤M├─╫────
                          └───┘ │ └───┘  │  └╥┘ ║
 padding_qb_1: ─────────────────┼────────┼───╫──╫────
               ┌─────────┐      │      ┌─┴─┐ ║  ║ ┌─┐
original_qb_2: ┤ Ry(0.7) ├──────■───■──┤ Y ├─╫──╫─┤M├
               └─────────┘        ┌─┴─┐└┬─┬┘ ║  ║ └╥┘
original_qb_3: ───────────────────┤ X ├─┤M├──╫──╫──╫─
                                  └───┘ └╥┘  ║  ║  ║
           c0: ══════════════════════════╬═══╬══╩══╬═
                                         ║   ║     ║
           c1: ══════════════════════════╬═══╩═════╬═
                                         ║         ║
           c2: ══════════════════════════╬═════════╩═
                                         ║
           c3: ══════════════════════════╩═══════════

5. Gate conversion passes#

convert_to_cz#

Converts 2-qubit gates (such as cx, cy, swap) into CZ-based forms.

convert_to_prx#

Converts single-qubit operations into PRX-style decomposition used in IQM-related flows.

These are typically used near the end of a transpilation pipeline.

pm_convert = PassManager()
pm_convert += convert_to_cz()
pm_convert += convert_to_prx

converted_qc = pm_convert.run(build_demo_circuit())
print(converted_qc)

Output:

               ┌──────────────┐┌────────┐                                »
original_qb_0: ┤ R(3π/2,-π/2) ├┤ R(π,0) ├─■──────────────────────────────»
               ├──────────────┤├────────┤ │ ┌──────────────┐┌────────┐   »
original_qb_1: ┤ R(3π/2,-π/2) ├┤ R(π,0) ├─■─┤ R(3π/2,-π/2) ├┤ R(π,0) ├─■─»
               └┬────────────┬┘└────────┘   └──────────────┘└────────┘ │ »
original_qb_2: ─┤ R(0.7,π/2) ├─────────────────────────────────────────■─»
               ┌┴────────────┴┐┌────────┐                                »
original_qb_3: ┤ R(3π/2,-π/2) ├┤ R(π,0) ├────────────────────────────────»
               └──────────────┘└────────┘                                »
           c0: ══════════════════════════════════════════════════════════»
                                                                         »
           c1: ══════════════════════════════════════════════════════════»
                                                                         »
           c2: ══════════════════════════════════════════════════════════»
                                                                         »
           c3: ══════════════════════════════════════════════════════════»
                                                                         »
«                                                                        »
«original_qb_0: ─────────────────────────────────────────────────────────»
«               ┌────────┐  ┌──────────┐              ┌─┐                »
«original_qb_1: ┤ R(π,0) ├──┤ R(π,π/4) ├──────────────┤M├────────────────»
«               └────────┘  ├──────────┤  ┌──────────┐└╥┘┌──────────────┐»
«original_qb_2: ────■───────┤ R(π,π/2) ├──┤ R(π,π/4) ├─╫─┤ R(3π/2,-π/2) ├»
«                   │     ┌─┴──────────┴─┐└┬────────┬┘ ║ └─────┬─┬──────┘»
«original_qb_3: ────■─────┤ R(3π/2,-π/2) ├─┤ R(π,0) ├──╫───────┤M├───────»
«                         └──────────────┘ └────────┘  ║       └╥┘       »
«           c0: ═══════════════════════════════════════╬════════╬════════»
«                                                      ║        ║        »
«           c1: ═══════════════════════════════════════╩════════╬════════»
«                                                               ║        »
«           c2: ════════════════════════════════════════════════╬════════»
«                                                               ║        »
«           c3: ════════════════════════════════════════════════╩════════»
«                                                                        »
«                                            ┌─┐┌────────┐          »
«original_qb_0: ───────────■─────────────────┤M├┤ gphase ├──────────»
«                          │                 └╥┘└────────┘          »
«original_qb_1: ───────────┼──────────────────╫─────────────────────»
«               ┌────────┐ │ ┌──────────────┐ ║ ┌────────┐┌────────┐»
«original_qb_2: ┤ R(π,0) ├─■─┤ R(3π/2,-π/2) ├─╫─┤ R(π,0) ├┤ R(π,0) ├»
«               └────────┘   └──────────────┘ ║ └────────┘└────────┘»
«original_qb_3: ──────────────────────────────╫─────────────────────»
«                                             ║                     »
«           c0: ══════════════════════════════╩═════════════════════»
«                                                                   »
«           c1: ════════════════════════════════════════════════════»
«                                                                   »
«           c2: ════════════════════════════════════════════════════»
«                                                                   »
«           c3: ════════════════════════════════════════════════════»
«                                                                   »
«
«original_qb_0: ───────────────
«
«original_qb_1: ───────────────
«               ┌──────────┐┌─┐
«original_qb_2: ┤ R(π,π/4) ├┤M├
«               └──────────┘└╥┘
«original_qb_3: ─────────────╫─
«                            ║
«           c0: ═════════════╬═
«                            ║
«           c1: ═════════════╬═
«                            ║
«           c2: ═════════════╩═
«
«           c3: ═══════════════
«

6. Tuning effort and depth_weight#

Both plasma_layout and plasma_route accept two knobs that let you trade off compilation time against result quality, and gate count against circuit depth:

Parameter

Effect

effort

Controls how many random seeds / candidates the router explores. Higher values find better solutions but take longer.

depth_weight

Steers the optimization target on a scale from −1 (minimize gate/SWAP count) through 0 (balanced) to +1 (minimize depth).

To see these in action, we compile a non-trivial circuit — a 5-bit quantum adder — onto a 4 × 4 square-grid topology and compare three settings.

The decompose pass#

Before layout and routing, the compiled adder circuit contains multi-controlled gates (> 2 qubits). These cannot be placed directly onto hardware. The decompose pass recursively breaks down any gate satisfying a predicate (here: more than 2 qubits) into 1- and 2-qubit gates. We include it in the pipeline so the router only sees hardware-compatible operations.

from qrisp import QuantumFloat, decompose

# Build a 5-bit quantum adder circuit
a = QuantumFloat(5)
b = QuantumFloat(5)
a += b
qc = a.qs.compile()

# 4x4 square grid coupling map
N = 4
connectivity = []
for i in range(N**2):
    if i % N:
        connectivity.append((i, i - 1))
    if i > N:
        connectivity.append((i, i - N))

def route_with_settings(depth_weight, effort):
    pm = PassManager()
    pm += decompose(decompose_predicate = lambda op: op.num_qubits > 2)
    pm += plasma_layout(connectivity=connectivity, depth_weight=depth_weight, effort=effort)
    pm += plasma_route(connectivity=connectivity, depth_weight=depth_weight, effort=effort)
    compiled = pm.run(qc)
    return compiled

# --- Run 1: depth_weight = -1  (minimize gate count) ---
qc_gateopt = route_with_settings(depth_weight=-1, effort=10)
print("depth_weight = -1, effort = 10  (minimize gate count)")
print(f"  CNOT depth : {qc_gateopt.cnot_depth()}")
print(f"  Gate counts: {qc_gateopt.count_ops()}")

# --- Run 2: depth_weight = +1  (minimize depth) ---
qc_depthopt = route_with_settings(depth_weight=1, effort=10)
print("\ndepth_weight = +1, effort = 10  (minimize depth)")
print(f"  CNOT depth : {qc_depthopt.cnot_depth()}")
print(f"  Gate counts: {qc_depthopt.count_ops()}")

# --- Run 3: depth_weight = +1, higher effort ---
qc_depthopt_hi = route_with_settings(depth_weight=1, effort=1000)
print("\ndepth_weight = +1, effort = 1000 (minimize depth, try harder)")
print(f"  CNOT depth : {qc_depthopt_hi.cnot_depth()}")
print(f"  Gate counts: {qc_depthopt_hi.count_ops()}")

Output:

depth_weight = -1, effort = 10  (minimize gate count)
  CNOT depth : 55
  Gate counts: {'h': 10, 'cx': 50, 'p': 53, 'swap': 9}

depth_weight = +1, effort = 10  (minimize depth)
  CNOT depth : 52
  Gate counts: {'h': 10, 'cx': 50, 'p': 53, 'swap': 10}

depth_weight = +1, effort = 1000 (minimize depth, try harder)
  CNOT depth : 46
  Gate counts: {'h': 10, 'cx': 50, 'p': 53, 'swap': 11}

What to observe#

  • ``depth_weight = -1`` produces the fewest SWAP gates (lowest total gate count) but the deepest circuit — the router packs qubits tightly even if that serialises operations.

  • ``depth_weight = +1`` trades extra SWAPs for a shallower circuit: the CNOT depth drops, while the SWAP / gate count increases.

  • Raising ``effort`` to 1000 at depth_weight = +1 pushes the depth even lower — at the cost of yet more SWAPs and longer compilation time. The router explores more candidates and finds increasingly aggressive parallelisation strategies.

In general, use depth_weight = -1 when gate count (and thus error rate) matters most, and depth_weight = +1 when circuit duration (i.e. thermal decay) on hardware is the bottleneck. effort controls how long you’re willing to wait for a better solution.

7. End-to-end IQM transpilation: transpile_to_iqm#

transpile_to_iqm wraps the full plasma-sabre pipeline — layout, routing, gate conversion, and several additional optimization passes — into a single call. Because it bundles these strong optimizations together, it should be considered the default function for production-level transpilation when targeting IQM hardware.

Note

The public function name is transpile_to_iqm (lowercase).

# Local transpilation example against a known coupling map
iqm_ready_qc = transpile_to_iqm(build_demo_circuit(), connectivity=connectivity)
print(iqm_ready_qc)

Output:

padding_qb_0: ─────────────────────────────────────────────────────────────»
                                                                            »
padding_qb_10: ─────────────────────────────────────────────────────────────»
               ┌─────────────┐   ┌────────────┐      ┌─┐                    »
original_qb_1: ┤ R(π/2,-π/2) ├─■─┤ R(π/2,π/2) ├─■────┤M├────────────────────»
               ├─────────────┤ │ └────────────┘ │    └╥┘   ┌────────────┐   »
original_qb_0: ┤ R(π/2,-π/2) ├─■────────────────┼─────╫──■─┤ R(π/2,π/2) ├─■─»
               └─────────────┘                  │     ║  │ └────────────┘ │ »
padding_qb_1:  ─────────────────────────────────┼─────╫──┼────────────────┼─»
                                                │     ║  │                │ »
padding_qb_3:  ─────────────────────────────────┼─────╫──┼────────────────┼─»
                ┌────────────┐                  │     ║  │  ┌──────────┐  │ »
original_qb_2: ─┤ R(0.7,π/2) ├──────────────────■──■──╫──┼──┤ R(π/2,0) ├──┼─»
               ┌┴────────────┤                     │  ║  │ ┌┴──────────┴┐ │ »
original_qb_3: ┤ R(π/2,-π/2) ├─────────────────────■──╫──■─┤ R(π/2,π/2) ├─■─»
               └─────────────┘                        ║    └────────────┘   »
padding_qb_9:  ───────────────────────────────────────╫─────────────────────»
                                                      ║                     »
padding_qb_7:  ───────────────────────────────────────╫─────────────────────»
                                                      ║                     »
padding_qb_6:  ───────────────────────────────────────╫─────────────────────»
                                                      ║                     »
padding_qb_2:  ───────────────────────────────────────╫─────────────────────»
                                                      ║                     »
padding_qb_5:  ───────────────────────────────────────╫─────────────────────»
                                                      ║                     »
padding_qb_8:  ───────────────────────────────────────╫─────────────────────»
                                                      ║                     »
padding_qb_4:  ───────────────────────────────────────╫─────────────────────»
                                                      ║                     »
padding_qb_11: ───────────────────────────────────────╫─────────────────────»
                                                      ║                     »
        c0:    ═══════════════════════════════════════╬═════════════════════»
                                                      ║                     »
        c1:    ═══════════════════════════════════════╩═════════════════════»
                                                                            »
        c2:    ═════════════════════════════════════════════════════════════»
                                                                            »
        c3:    ═════════════════════════════════════════════════════════════»
                                                                            »
«
« padding_qb_0: ──────────────────────────────────────────────────────
«
«padding_qb_10: ──────────────────────────────────────────────────────
«
«original_qb_1: ──────────────────────────────────────────────────────
«               ┌─────────────┐                 ┌─┐
«original_qb_0: ┤ R(π/2,-π/2) ├─■───────────────┤M├───────────────────
«               └─────────────┘ │               └╥┘
« padding_qb_1: ────────────────┼────────────────╫────────────────────
«                               │                ║
« padding_qb_3: ────────────────┼────────────────╫────────────────────
«                               │                ║    ┌───────────┐┌─┐
«original_qb_2: ────────────────┼────────────────╫──■─┤ R(π/2,-π) ├┤M├
«               ┌─────────────┐ │ ┌────────────┐ ║  │ └────┬─┬────┘└╥┘
«original_qb_3: ┤ R(π/2,-π/2) ├─■─┤ R(π/2,π/2) ├─╫──■──────┤M├──────╫─
«               └─────────────┘   └────────────┘ ║         └╥┘      ║
« padding_qb_9: ─────────────────────────────────╫──────────╫───────╫─
«                                                ║          ║       ║
« padding_qb_7: ─────────────────────────────────╫──────────╫───────╫─
«                                                ║          ║       ║
« padding_qb_6: ─────────────────────────────────╫──────────╫───────╫─
«                                                ║          ║       ║
« padding_qb_2: ─────────────────────────────────╫──────────╫───────╫─
«                                                ║          ║       ║
« padding_qb_5: ─────────────────────────────────╫──────────╫───────╫─
«                                                ║          ║       ║
« padding_qb_8: ─────────────────────────────────╫──────────╫───────╫─
«                                                ║          ║       ║
« padding_qb_4: ─────────────────────────────────╫──────────╫───────╫─
«                                                ║          ║       ║
«padding_qb_11: ─────────────────────────────────╫──────────╫───────╫─
«                                                ║          ║       ║
«           c0: ═════════════════════════════════╬══════════╩═══════╬═
«                                                ║                  ║
«           c1: ═════════════════════════════════╬══════════════════╬═
«                                                ║                  ║
«           c2: ═════════════════════════════════╬══════════════════╩═
«                                                ║
«           c3: ═════════════════════════════════╩════════════════════
«

8. Actual IQM backend call (with token placeholder)#

This section demonstrates real hardware submission.

  1. Install IQM client package (iqm-client) if needed

  2. Set your token and server URL

  3. Fetch architecture, transpile, submit

If token is left as placeholder, the code prints instructions and skips submission.

from iqm.qrisp_iqm import IQMBackend, create_iqm_pass_manager

token = "YOUR_TOKEN_HERE"
server_url = "https://resonance.iqm.tech"

if token == "YOUR_TOKEN_HERE":
    print("Set your real IQM token in `token` to run this cell.")
else:
    garnet = IQMBackend(device_instance="garnet", # Select garnet
                        server_url=server_url,
                        token=token, # Authenticate
                        pass_manager = PassManager()) # Create an empty pass manager to ensure circuits are passed to the backend as is

    iqm_connectivity = garnet.connectivity

    garnet.pm += create_iqm_pass_manager(connectivity=iqm_connectivity,
                                         effort=100,
                                         depth_weight=0.0,)

    result_counts = garnet.run(build_demo_circuit(), shots=100)

    print("IQM result counts:")
    print(result_counts)

Output (with a valid token):

IQM result counts:
{'0000': 41, '0011': 5, '0100': 2, '0110': 2, '0111': 2, '1001': 2, '1010': 1, '1011': 1, '1100': 2, '1101': 7, '1110': 35}