Snowflurry™ v0.2.0 is released!


Snowflurry™ v0.2.0 is ready to download!
Snowflurry™ ❄️ v0.2.0 is our second release of SnowflurrySDK in 2024. We have added a few cool features in this release.
Introducing projects and realms
To facilitate using cloud platforms, we have introduced two new concepts for job submission. To be able to share the same quantum hardware among different cloud platforms or user groups while maintaining data privacy, we now have to use a realm
variable when submitting a job. The realm
makes sure that user information from different organizations is stored and authenticated separately.
project_id
is also a new concept. When specifying a Quantum Processing Unit (QPU), the project_id
field controls which projects the jobs sent to this device belong to.
When the jobs are billed to the user, the billing occurs separately for each project.
That way, a large research group with jobs related to different endeavors can separate those jobs easily.
Some users may be authorized to submit to only one or several projects.
Let's demonstrate how the realm
and project_id
fields are defined and used.
In order to avoid hard-coding credentials, we recommend using the following ENV variables, which can be declared in a shell:
export THUNDERHEAD_USER=username
export THUNDERHEAD_API_TOKEN=not-a-real-access-token
export THUNDERHEAD_HOST=http://thunderhead.anyonsys.com
export THUNDERHEAD_PROJECT_ID=my-project
export THUNDERHEAD_REALM=my-realm
Then, the following script can be run on Julia, which declares an AnyonYukonQPU
object.
using Snowflurry
user = ENV["THUNDERHEAD_USER"]
token = ENV["THUNDERHEAD_API_TOKEN"]
host = ENV["THUNDERHEAD_HOST"]
project_id = ENV["THUNDERHEAD_PROJECT_ID"]
realm = ENV["THUNDERHEAD_REALM"]
qpu = AnyonYukonQPU(
host = host,
user = user,
access_token = token,
project_id = project_id,
realm = realm,
)
The output of the last command prints the description of our QPU device:
Quantum Processing Unit:
manufacturer: Anyon Systems Inc.
generation: Yukon
serial_number: ANYK202201
project_id: my-project
qubit_count: 6
connectivity_type: linear
realm: my-realm
Now, let's create a QuantumCircuit
to send to this QPU, which produces the famed Bell state:
circuit = QuantumCircuit(qubit_count = 6)
push!(circuit, hadamard(1))
for i in 1:5
push!(circuit, control_x(i, i + 1))
end
for i in 1:6
push!(circuit, readout(i, i))
end
print(circuit)
Those commands create the following circuit, in which qubit 1 is put in a superposition of |0⟩ and |1⟩, and then all other qubits are entangled with it. Finally, a readout is attached to each qubit, placing the measurement in a classical registry.
Quantum Circuit Object:
qubit_count: 6
bit_count: 6
q[1]:──H────*────────────────────────✲───────────────────────────
|
q[2]:───────X────*────────────────────────✲──────────────────────
|
q[3]:────────────X────*────────────────────────✲─────────────────
|
q[4]:─────────────────X────*────────────────────────✲────────────
|
q[5]:──────────────────────X────*────────────────────────✲───────
|
q[6]:───────────────────────────X─────────────────────────────✲──
Let's now send this job over to our QPU:
shot_count=100
transpile_and_run_job(qpu, circuit, shot_count)
Which returns the results of our job:
Dict("000000" => 46, "111111" => 54)
For more details, see Snowflurry documentation.
Track global phase in operators
Previously, the usual rotation_z
was omitted from Snowflurry, as it is equivalent to a phase_shift
up to a global phase.
In release v0.2.0, the rotation_z
gate can also be used in QuantumCircuits, in which half the phase offset is applied to state |0⟩ and half to state |1⟩:
julia> phase_offset = pi/2
julia> rotation_z(1, phase_offset)
Gate Object: Snowflurry.RotationZ
Parameters:
lambda : 1.5707963267948966
Connected_qubits : [1]
Operator:
(2,2)-element Snowflurry.DiagonalOperator:
Underlying data type: ComplexF64:
0.7071067811865476 - 0.7071067811865475im .
. 0.7071067811865476 + 0.7071067811865475im
This can be contrasted with the phase_shift
gate, where all the phase offset is applied to the |1⟩:
julia> phase_shift(1, phase_offset)
Gate Object: Snowflurry.PhaseShift
Parameters:
lambda : 1.5707963267948966
Connected_qubits : [1]
Operator:
(2,2)-element Snowflurry.DiagonalOperator:
Underlying data type: ComplexF64:
1.0 + 0.0im .
. 6.123233995736766e-17 + 1.0im
Controlled Parametric Gates
Snowflurry can now transpile controlled parametric gates. This feature was added by popular demand, and we are happy to have it implemented.
A Controlled
gate consists of a kernel
gate, which can be of any type other
than Controlled
, and a number of control
qubits.
For the time being, transpilation (the process of turning a circuit into an equivalent one, using gates supported by a hardware QPU for instance) is only implemented for single-target, single-control Controlled
gates.
For Controlled
gates, the global phase offset applied by the kernel
to a single qubit does become important, as the multi-qubit process of the Controlled
gate turns this into a relative phase between the qubits it interacts with.
As an illustration of this, let's contrast the output of transpilation of a Controlled{PhaseShift}
and a Controlled{RotationZ}
.
julia> phase_offset = pi/2
1.5707963267948966
julia> controlled_phase_shift = controlled(phase_shift(2, phase_offset), [1])
Gate Object: Controlled{Snowflurry.PhaseShift}
Parameters:
lambda : 1.5707963267948966
Connected_qubits : [1, 2]
Operator:
(4, 4)-element Snowflurry.DenseOperator:
Underlying data ComplexF64:
1.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im
0.0 + 0.0im 1.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im
0.0 + 0.0im 0.0 + 0.0im 1.0 + 0.0im 0.0 + 0.0im
0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im 6.123233995736766e-17 + 1.0im
julia> controlled_rotation_z = controlled(rotation_z(2, phase_offset), [1])
Gate Object: Controlled{Snowflurry.RotationZ}
Parameters:
lambda : 1.5707963267948966
Connected_qubits : [1, 2]
Operator:
(4, 4)-element Snowflurry.DenseOperator:
Underlying data ComplexF64:
1.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im
0.0 + 0.0im 1.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im
0.0 + 0.0im 0.0 + 0.0im 0.7071067811865476 - 0.7071067811865475im 0.0 + 0.0im
0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im 0.7071067811865476 + 0.7071067811865475im
Now, if circuits containing those controlled gates are transpiled (which occurs every time transpile_and_run_job()
is executed), the following sequence of gates is produced in each case:
julia> transpile(get_transpiler(qpu),
QuantumCircuit(qubit_count=2, instructions=[controlled_phase_shift, readout(2,2)]))
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:───────────────────────*───────────────────────────────────────────*────T────────────────────────────
| |
q[2]:──T────X_90────Z_90────Z────Z_m90────X_90────T────X_m90────Z_90────Z─────────Z_90────X_90────Z────✲──
julia> transpile(get_transpiler(qpu),
QuantumCircuit(qubit_count=2, instructions=[controlled_rotation_z, readout(2,2)]))
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:───────────────────────*───────────────────────────────────────────*────────────────────────────
| |
q[2]:──T────X_90────Z_90────Z────Z_m90────X_90────T────X_m90────Z_90────Z────Z_90────X_90────Z────✲──
Observe that the former circuit (made with controlled_phase_shift
) has an additional T
gate (or a pi_8
) gate on qubit 1, which has the effect of introducing a relative phase between qubits 1 and 2 of π/4
,
In the future, we plan on implementing the transpilation of Controlled
gates involving multiple targets, and multiple controls.
For more details on the changes in this release, feel free to examine the change logs.
Remember that you can find Snowflurry at:
Github: https://github.com/SnowflurrySDK/Snowflurry.jl
JuliaHub: https://juliahub.com/ui/Packages/General/Snowflurry
You can always reach us at [email protected] or join the discussion on Github.