Introduction to Helmi with Cirq¶
Helmi is a 5 qubit Quantum Computer that is co-developed by VTT and IQM. It uses superconducting transmon qubits in a star shaped topology. Helmi's natives gates consist of the phased-rx and controlled-z gates. This architecture is called Adonis by IQM.
In this tutorial running on Helmi is demonstrated using the Cirq framework. You can also run on Helmi using Qiskit with the qiskit-on-iqm adapter, and this is described in a separate notebook.
Here is Helmi!
Using Helmi with Cirq¶
First we import cirq-on-iqm which is needed to run on Helmi with cirq. You can read the user guide here.
import cirq
import matplotlib.pyplot as plt
import networkx as nx
import numpy as np
from cirq.contrib.svg import SVGCircuit
from iqm.cirq_iqm.iqm_sampler import IQMSampler
from iqm.cirq_iqm.optimizers import simplify_circuit
Then connection to the backend is simple! For this we point the IQMSampler
at what is called "cocos URL". The cocos url to access Helmi is provided below.
sampler = IQMSampler("https://qc.vtt.fi/helmi/cocos")
device = sampler.device
We can also specify a calibration_set_id
and whether to run with max_circuit_duration_over_t2
or not. The max_circuit_duration_over_t2
option when set to 1.0
disqualifies any circuits that are too long compared to the coherence time of the qubits.
sampler = IQMSampler(
"https://qc.vtt.fi/helmi/cocos",
calibration_set_id="2318283e-3adf-4a88-a936-a33affde4af7",
max_circuit_duration_over_t2=1.0,
heralding_mode="zeros",
)
Now that we have connected to Helmi, we can query for some information about Helmi!
print(f"Native operations: {device.metadata.gateset}")
print(f"Number of qubits: {device.qubits}")
print(f"Coupling map: {device.metadata.nx_graph.edges}")
The topology can be visualized with networkx
.
G = nx.Graph()
G.add_edges_from(device.metadata.nx_graph.edges)
node_labels = {node: str(node) for node in G.nodes}
nx.draw(
G,
labels=node_labels,
node_color="skyblue",
node_size=500,
font_size=10,
with_labels=True,
)
Constructing and executing quantum circuits¶
Circuits are constructed in Cirq by decomposing and routing them for the target topology. Additionally, you can run some simple optimization routines to get better performance for your circuit.
q1, q2 = cirq.NamedQubit("Alice"), cirq.NamedQubit("Bob")
circuit = cirq.Circuit()
circuit.append(cirq.H(q1))
circuit.append(cirq.CNOT(q1, q2))
circuit.append(cirq.measure(q1, q2, key="m"))
SVGCircuit(circuit)
First we'll transpile the circuit into Helmi's native gates
decomposed_circuit = device.decompose_circuit(circuit)
SVGCircuit(decomposed_circuit)
Then we route the circuit based on Helmi's topology
routed_circuit, initial_mapping, final_mapping = device.route_circuit(decomposed_circuit)
SVGCircuit(routed_circuit)
By printing the initial mapping we can see how the qubit names have beemn translated into the names of the qubits physically on Helmi and how the original qubit names were routed onto the device.
print(initial_mapping)
This circuit can be executed on Helmi, but as an additional step we can simplify the circuit, using cirq-on-iqm's built in optimizers.
simplified_circuit = simplify_circuit(routed_circuit)
SVGCircuit(simplified_circuit)
The circuits can then be executed by calling sampler.run
. Additionally, a folding function can be passed to process the sampled measurement results and convert the results into a format suitable for plotting for example.
def fold_func(x: np.ndarray) -> str:
"""Fold the measured bit arrays into strings."""
return "".join(map(lambda x: chr(x + ord("0")), x))
result = sampler.run(simplified_circuit, repetitions=100)
# print(result.measurements['m'])
print(result.histogram(key="m", fold_func=fold_func))
A histogram of the results can be plotted using plot_state_histogram
.
def binary_labels(num_qubits):
return [bin(x)[2:].zfill(num_qubits) for x in range(2**num_qubits)]
cirq.plot_state_histogram(result, plt.subplot(), tick_label=binary_labels(2))
Additional metadata about the executed job can also be found.
print("Job ID: ", result.metadata.job_id) # Retrieving the submitted job id
print(
"Calibration Set ID: ", result.metadata.calibration_set_id
) # Retrieving the current calibration set id.
Batch execution¶
Helmi also allows for batches of circuits to be submitted with 1 call to the quantum computer. A batch is simply a list of circuits. This is often faster than executing circuits individually, however, circuits will still be executed sequentially. On Helmi currently you can only place a maximum of 20 circuits in one batch. All circuits in a batch are executed with the same number of shots. The maximum number of shots per circuit is 100,000.
With cirq this is implemented via the run_iqm_batch
method of sampler
.
Batch submission of circuits allows parameterized circuits to be executed using the cirq-resolve_parameters
function.
circuit_list = []
q1, q2 = cirq.NamedQubit("Alice"), cirq.NamedQubit("Bob")
circuit_1 = cirq.Circuit()
circuit_1.append(cirq.H(q1))
circuit_1.append(cirq.CNOT(q1, q2))
circuit_1.append(cirq.measure(q1, q2, key="m"))
SVGCircuit(circuit_1)
circuit_2 = cirq.Circuit()
circuit_2.append(cirq.H(q1))
circuit_2.append(cirq.CNOT(q2, q1))
circuit_2.append(cirq.measure(q1, q2, key="m"))
SVGCircuit(circuit_2)
routed_circuit_1, _, _ = device.route_circuit(device.decompose_circuit(circuit_1))
routed_circuit_2, _, _ = device.route_circuit(device.decompose_circuit(circuit_2))
circuit_list.append(routed_circuit_1)
circuit_list.append(routed_circuit_2)
results = sampler.run_iqm_batch(circuit_list, repetitions=10)
for result in results:
print(result.histogram(key="m"))
Summary¶
In this notebook we have demonstrated how to connect and run circuits on Helmi with Cirq using the cirq-on-iqm adapter.
Additional Reading¶
- Long-distance transmon coupler with CZ gate fidelity above 99.8%. Paper by IQM describing the superconducting technology behind Helmi.
- Helmi press release.
- cirq-on-iqm Github page.
- cirq-on-iqm documentation. We are running
11.13
. - CSC documentation on Helmi.