Qdislib — 09 · Distributed execution & real QPUs¶
The whole point of cutting is to run the pieces in parallel — across a cluster with PyCOMPSs, and/or on real quantum hardware. The cells below are illustrative: they need a PyCOMPSs runtime, IBM credentials, or the BSC QPU environment, so they aren’t executed in the docs build.
1. Distributed with PyCOMPSs¶
The API is identical — under PyCOMPSs each expectation_value runs as a COMPSs task and *_subcircuits returns serializable Subcircuit wrappers automatically. In a notebook you start the runtime interactively:
[ ]:
import pycompss.interactive as ipycompss
ipycompss.start(graph=True, monitor=1000)
import Qdislib as qd
from qibo import models, gates
circuit = models.Circuit(5)
circuit.add(gates.RY(0, 0.8)); circuit.add(gates.CZ(0, 1)); circuit.add(gates.RY(1, 0.5))
circuit.add(gates.CZ(1, 2)); circuit.add(gates.RY(2, 0.6)); circuit.add(gates.CZ(2, 3))
circuit.add(gates.RY(3, 0.4)); circuit.add(gates.CZ(3, 4)); circuit.add(gates.RX(4, 0.3))
cut = qd.find_cut(circuit, gate_cut=True, wire_cut=False)
subcircuits = qd.gate_cutting_subcircuits(circuit, cut, software="qibo")
values = [qd.expectation_value(s) for s in subcircuits] # scheduled as tasks
print(subcircuits.reconstruct(values))
ipycompss.stop(sync=True)
For batch jobs, run a plain script under runcompss instead — see `examples/compss_app.py <https://gitlab.bsc.es/wdc/quantum/qdislib>`__ and examples/README.md. Nothing in your Qdislib code changes.
2. IBM Quantum¶
With Qdislib[qiskit] and an IBM Quantum account (get a token at quantum.ibm.com), set your credentials as environment variables and run the cut subcircuits on real IBM hardware with qpu=True.
[ ]:
import os
# Required:
os.environ["IBM_QUANTUM_TOKEN"] = "<your_token>"
# Optional:
os.environ["IBM_QUANTUM_INSTANCE"] = "<your_instance>"
os.environ["IBM_QUANTUM_CHANNEL"] = "ibm_cloud" # or "ibm_quantum"
os.environ["IBM_QUANTUM_MAX_TIME"] = "60" # batch session, seconds
os.environ["IBM_QUANTUM_QPU_NAME"] = "ibm_marrakesh" # omit -> least-busy backend
If IBM_QUANTUM_QPU_NAME is unset, Qdislib runs on the least-busy real backend (service.least_busy(operational=True, simulator=False)).
[ ]:
import Qdislib as qd
from qiskit import QuantumCircuit
circuit = QuantumCircuit(10)
for i in range(9):
circuit.cz(i, i + 1)
# find_cut's IBM path uses Qiskit's hardware-aware cut finder:
cut = qd.find_cut(circuit, max_qubits=5, implementation="ibm-ckt")
# qpu_dict maps a QPU to its qubit count (the chip's maximum).
value = qd.gate_cutting(circuit, cut, software="qiskit",
qpu=True, qpu_dict={"IBM_Quantum": 127})
print(value)
3. BSC QPUs (cross-cluster bridge)¶
At BSC the classical (PyCOMPSs) job and the quantum-control job run on separate clusters. They communicate through a shared filesystem directory: a listener on the quantum cluster executes submitted subcircuits on a QPU (Blue or Red), while the cut job submits them from the classical side. Both agree on $QDISLIB_SUBCIRCUITS_DIR.
On the quantum cluster (see examples/qpu_listener.py):
[ ]:
import Qdislib as qd
# $QDISLIB_SUBCIRCUITS_DIR and $RUNCARD_RED must be set.
qd.run_qpu_listener(qpu="red", nshots=1000) # runs until a STOP file / Ctrl-C
On the classical cluster, point a cut at the QPU (same shared directory):
[ ]:
import Qdislib as qd
from qibo import models, gates
circuit = models.Circuit(5)
circuit.add(gates.RY(0, 0.8)); circuit.add(gates.CZ(0, 1)); circuit.add(gates.RY(1, 0.5))
circuit.add(gates.CZ(1, 2)); circuit.add(gates.RY(2, 0.6)); circuit.add(gates.CZ(2, 3))
circuit.add(gates.RY(3, 0.4)); circuit.add(gates.CZ(3, 4)); circuit.add(gates.RX(4, 0.3))
cut = qd.find_cut(circuit, gate_cut=True, wire_cut=False)
value = qd.gate_cutting(circuit, cut, software="qibo",
qpu=True, qpu_dict={"MN_Ona": 5})
print(value)
You’ve finished the learning path 🎉¶
You can now cut a circuit, run it (serially, distributed, or on a QPU), choose a backend, measure arbitrary observables, and cache results. See the API reference for the full surface.