AnyonLabs™

Snowflurry™ v0.2.0 is released!

Cover Image for 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.