Tutorials
Running a Circuit on Real Hardware
In the previous tutorial, we learnt how to run a quantum circuit on a virtual QPU. We also learnt that every QPU driver should adhere to the AbstractQPU
API.
In this tutorial, we will learn how to submit a job to real hardware. At the moment, we have only implemented the driver for Anyon's quantum processors but we welcome contributions from other members of the community, as well as other hardware vendors to use Snowflurry
with a variety of machines.
Anyon QPU
Note
This tutorial is written for the selected partners and users who have been granted access to Anyon's hardware.
The current release of Snowflurry
supports Anyon's Yukon quantum processor (see AnyonYukonQPU
) which is made from an array of 6 tunable superconducting transmon qubits interleaved with 5 tunable couplers. The following generation of QPU, called AnyonYamaskaQPU
is also implemented, in which 12 qubits are arranged in a lattice, along with 14 couplers.
We can start by defining a qpu
variable to point to the host computer that will queue jobs on the quantum processor and provide it with user credentials. A valid project_id is also required to submit jobs on Anyon infrastructure:
using Snowflurry
user = ENV["THUNDERHEAD_USER"]
token = ENV["THUNDERHEAD_API_TOKEN"]
host = ENV["THUNDERHEAD_HOST"]
project = ENV["THUNDERHEAD_PROJECT_ID"]
realm = ENV["THUNDERHEAD_REALM"]
qpu = AnyonYukonQPU(host = host, user = user, access_token = token, project_id = project, realm = realm)
Keep your credentials safe!
If you plan to make your code public or work in a shared environment, it is best to use environment variables to set the user credentials rather than hardcoding them!
We can now print the qpu
object to print further information about the hardware:
println(qpu)
# output
Quantum Processing Unit:
manufacturer: Anyon Systems Inc.
generation: Yukon
serial_number: ANYK202201
project_id: test-project
qubit_count: 6
connectivity_type: linear
realm: test-realm
Alternatively, one can use the get_metadata
function to obtain a Dict
object corresponding to the QPU information:
get_metadata(qpu)
# output
Dict{String, Union{Int64, String}} with 7 entries:
"qubit_count" => 6
"generation" => "Yukon"
"manufacturer" => "Anyon Systems Inc."
"realm" => "test-realm"
"serial_number" => "ANYK202201"
"project_id" => "test-project"
"connectivity_type" => "linear"
We now continue to build a small circuit to create a Bell state as was presented in the previous tutorials:
c = QuantumCircuit(qubit_count = 2)
push!(c, hadamard(1), control_x(1, 2), readout(1, 1), readout(2, 2))
# output
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──H────*────✲───────
|
q[2]:───────X─────────✲──
Circuit Transpilation
The circuit above cannot be directly executed on the quantum processor. This is because the quantum processor only implements a set of native gates. This means that any arbitrary gate should first be transpiled into a set of native gates that can run on the QPU.
If you examine the src/anyon/anyon.jl
file, you notice that Anyon Yukon Processor implements the following set of native gates:
set_of_native_gates=[
PhaseShift,
Pi8,
Pi8Dagger,
SigmaX,
SigmaY,
SigmaZ,
X90,
XM90,
Y90,
YM90,
Z90,
ZM90,
ControlZ,
]
Snowflurry is designed to allow users to design and use their own transpilers for different QPUs. Alternatively, a user may opt not to use the default transpilers that are implemented for each QPU driver.
Let's see how we can transpile the above circuit, c
, to a circuit that can run on Anyon's QPU. We first define a transpiler
object that refers to the default transpiler for AnyonYukonQPU which shipped with Snowflurry
:
transpiler = get_transpiler(qpu)
Next, let's transpile the original circuit:
c_transpiled = transpile(transpiler, c)
# output
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──Z_90────────────X_90────Z_90────────────────────*────────────────────────────✲───────
|
q[2]:──────────Z_90────────────────────X_90────Z_90────Z────Z_90────X_90────Z_90─────────✲──
The final circuit c_transpiled
is now ready to be submitted to the QPU:
shot_count = 200
result = run_job(qpu, c_transpiled, shot_count)
println(result)
which should print something like:
Dict("11" => 97, "00" => 83, "01" => 11, "10" => 9)
The results show that the samples are mostly sampled between state $\left|00\right\rangle$ and $\left|11\right\rangle$. We do see some finite population in $\left|01\right\rangle$ and $\left|10\right\rangle$ that are due to the error in the computation. (The qubit ordering convention used is qubit number 1 on the left, with each following qubit to the right of it.)
Note
The user can skip the explicit transpiling step by using the transpile_and_run_job
function. This function will use the default transpiler of the QPU and then submit the job to the machine.