Library reference
Quantum Circuit
Snowflurry.QuantumCircuit
— Type.
QuantumCircuit(qubit_count::Int, bit_count::Int, instructions::Vector{AbstractInstruction}, name::String = "default")
QuantumCircuit(circuit::QuantumCircuit)
A data structure to represent a quantum circuit.
Fields
qubit_count::Int
– number of qubits (i.e., quantum register size).bit_count::Int
– Optional: number of classical bits (i.e., result register size). Defaults toqubit_count
if unspecified.instructions::Vector{AbstractInstruction}
– Optional: the sequence ofAbstractInstructions
(Gates
andReadouts
) that operate on qubits. Defaults to empty Vector.name::String
– Optional: name of the circuit job, used to identify it when sending to a hardware or virtual QPU.
Examples
julia> c = QuantumCircuit(qubit_count = 2)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:
q[2]:
A QuantumCircuit
can be initialized containing Gates
and Readouts
:
julia> c = QuantumCircuit(qubit_count = 2, instructions = [hadamard(1), sigma_x(2), control_x(1, 2), readout(1, 1), readout(2, 2)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──H─────────*────✲───────
|
q[2]:───────X────X─────────✲──
A deep copy of a QuantumCircuit
can be obtained with the following command:
julia> c_copy = QuantumCircuit(c)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──H─────────*────✲───────
|
q[2]:───────X────X─────────✲──
Base.push!
— Function.
push!(circuit::QuantumCircuit, gates::AbstractGateSymbol...)
Inserts one or more gates
at the end of a circuit
.
A Vector
of AbstractGateSymbol
objects can be passed to this function by using splatting. More details about splatting are provided here.
Examples
julia> c = QuantumCircuit(qubit_count = 2);
julia> push!(c, hadamard(1), sigma_x(2))
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──H───────
q[2]:───────X──
julia> push!(c, control_x(1,2))
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──H─────────*──
|
q[2]:───────X────X──
julia> gate_list = [sigma_x(1), hadamard(2)];
julia> push!(c, gate_list...)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──H─────────*────X───────
|
q[2]:───────X────X─────────H──
Base.pop!
— Function.
pop!(circuit::QuantumCircuit)
Removes the last instruction from circuit.instructions
, and returns it.
Examples
julia> c = QuantumCircuit(qubit_count = 2);
julia> push!(c, hadamard(1), sigma_x(2))
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──H───────
q[2]:───────X──
julia> push!(c, control_x(1, 2))
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──H─────────*──
|
q[2]:───────X────X──
julia> pop!(c)
Gate Object: Snowflurry.ControlX
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.0 + 0.0im 1.0 + 0.0im
0.0 + 0.0im 0.0 + 0.0im 1.0 + 0.0im 0.0 + 0.0im
julia> c
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──H───────
q[2]:───────X──
Base.append!
— Function.
append!(base_circuit::QuantumCircuit, circuits_to_append::QuantumCircuit...)
Appends one or more circuits_to_append
to the base_circuit
.
The circuits_to_append
cannot contain more qubits than the base_circuit
.
Examples
julia> base = QuantumCircuit(qubit_count = 2, instructions = [sigma_x(1)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──X──
q[2]:─────
julia> append_1 = QuantumCircuit(qubit_count = 1, instructions = [sigma_z(1)])
Quantum Circuit Object:
qubit_count: 1
bit_count: 1
q[1]:──Z──
julia> append_2 = QuantumCircuit(qubit_count = 2, instructions = [control_x(1, 2)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──*──
|
q[2]:──X──
julia> append!(base, append_1, append_2)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──X────Z────*──
|
q[2]:────────────X──
Base.prepend!
— Function.
prepend!(base_circuit::QuantumCircuit, circuits_to_prepend::QuantumCircuit...)
Prepends one or more circuits_to_prepend
to the base_circuit
.
The order of the circuits_to_prepend
is maintained (i.e., circuits_to_prepend[1]
will appear leftmost in base_circuit
). The circuits_to_prepend
cannot contain more qubits than the base_circuit
.
Examples
julia> base = QuantumCircuit(qubit_count = 2, instructions = [sigma_x(1)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──X──
q[2]:─────
julia> prepend_1 = QuantumCircuit(qubit_count = 1, instructions = [sigma_z(1)])
Quantum Circuit Object:
qubit_count: 1
bit_count: 1
q[1]:──Z──
julia> prepend_2 = QuantumCircuit(qubit_count = 2, instructions = [control_x(1, 2)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──*──
|
q[2]:──X──
julia> prepend!(base, prepend_1, prepend_2)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──Z────*────X──
|
q[2]:───────X───────
Snowflurry.simulate
— Function.
simulate(circuit::QuantumCircuit)::Ket
Simulates and returns the wavefunction of the quantum device after running circuit
, assuming an initial state Ket ψ corresponding to the 0th Fock basis, i.e.: ψ = fock(0, 2^get_num_qubits(circuit))
.
Note
The input circuit
must not include Readouts
. For simulating circuits
including Readouts
, use simulate_shots
.
Employs the approach described in Listing 5 of Suzuki et. al. (2021).
Examples
julia> c = QuantumCircuit(qubit_count = 2);
julia> push!(c, hadamard(1))
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──H──
q[2]:─────
julia> push!(c, control_x(1, 2))
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──H────*──
|
q[2]:───────X──
julia> ket = simulate(c)
4-element Ket{ComplexF64}:
0.7071067811865475 + 0.0im
0.0 + 0.0im
0.0 + 0.0im
0.7071067811865475 + 0.0im
Snowflurry.simulate_shots
— Function.
simulate_shots(c::QuantumCircuit, shots_count::Int = 100)
Emulates a quantum computer by running a circuit for a given number of shots and returning measurement results, as prescribed by the Readouts
present in the circuit. The distribution of measured states corresponds to the coefficients in the resulting state Ket.
Examples
julia> c = QuantumCircuit(qubit_count = 2);
julia> push!(c, hadamard(1), control_x(1, 2), readout(1, 1), readout(2, 2))
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──H────*────✲───────
|
q[2]:───────X─────────✲──
julia> simulate_shots(c, 99)
99-element Vector{String}:
"11"
"00"
"11"
"11"
"11"
"11"
"11"
"00"
"00"
"11"
⋮
"00"
"00"
"11"
"00"
"00"
"00"
"00"
"00"
"00"
Snowflurry.get_measurement_probabilities
— Method.
get_measurement_probabilities(circuit::QuantumCircuit,
[target_qubits::Vector{<:Integer}])::AbstractVector{<:Real}
Returns a vector listing the measurement probabilities for the target_qubits
in the circuit
.
If no target_qubits
are provided, the probabilities are computed for all the qubits.
The measurement probabilities are listed from the smallest to the largest computational basis state. For instance, for a 2-qubit QuantumCircuit
, the probabilities are listed for $\left|00\right\rangle$, $\left|10\right\rangle$, $\left|01\right\rangle$, and $\left|11\right\rangle$.
Note
By convention, qubit 1 is the leftmost digit, followed by every subsequent qubit. $\left|10\right\rangle$ has qubit 1 in state $\left|1\right\rangle$ and qubit 2 in state $\left|0\right\rangle$
Examples
The following example constructs a QuantumCircuit
where the probability of measuring $\left|10\right\rangle$ is 50% and the probability of measuring $\left|11\right\rangle$ is also 50%.
julia> circuit = QuantumCircuit(qubit_count = 2);
julia> push!(circuit, hadamard(1), sigma_x(2))
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──H───────
q[2]:───────X──
julia> get_measurement_probabilities(circuit)
4-element Vector{Float64}:
0.0
0.4999999999999999
0.0
0.4999999999999999
For the same circuit
, the probability of measuring qubit 2 and finding 1 is 100%.
julia> target_qubit = [2];
julia> get_measurement_probabilities(circuit, target_qubit)
2-element Vector{Float64}:
0.0
0.9999999999999998
Base.inv
— Method.
inv(circuit::QuantumCircuit)
Return a QuantumCircuit
which is the inverse of the input circuit
. Each gate is replaced by its corresponding inverse, and the order of gates is reversed.
Examples
julia> c = QuantumCircuit(qubit_count = 2);
julia> push!(c, rotation_y(1, pi/4));
julia> push!(c, control_x(1, 2))
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──Ry(0.7854)────*──
|
q[2]:────────────────X──
julia> inv(c)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──*────Ry(-0.7854)──
|
q[2]:──X─────────────────
Snowflurry.get_num_gates_per_type
— Function.
get_num_gates_per_type(circuit::QuantumCircuit)::AbstractDict{<: AbstractString, <:Integer}
Returns a dictionary listing the number of gates of each type found in the circuit
.
The dictionary keys are the instruction_symbol of the gates while the values are the number of gates found.
Examples
julia> c = QuantumCircuit(qubit_count = 2);
julia> push!(c, hadamard(1), hadamard(2));
julia> push!(c, control_x(1, 2));
julia> push!(c, hadamard(2))
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──H─────────*───────
|
q[2]:───────H────X────H──
julia> get_num_gates_per_type(c)
Dict{String, Int64} with 2 entries:
"h" => 3
"cx" => 1
Snowflurry.get_num_gates
— Function.
get_num_gates(circuit::QuantumCircuit)::Integer
Returns the number of gates in the circuit
.
Examples
julia> c = QuantumCircuit(qubit_count = 2);
julia> push!(c, hadamard(1), hadamard(2));
julia> push!(c, control_x(1, 2))
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──H─────────*──
|
q[2]:───────H────X──
julia> get_num_gates(c)
3
Snowflurry.serialize_job
— Function.
serialize_job(circuit::QuantumCircuit,shot_count::Integer,host::String)
Creates a JSON-formatted String containing the circuit configuration to be sent to a QPU
service located at the URL specified by host
, along with the number of shots requested.
Examples
julia> c = QuantumCircuit(qubit_count = 2, instructions = [sigma_x(1)], name = "sigma_x job")
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──X──
q[2]:─────
julia> serialize_job(c, 10, "http://example.anyonsys.com", "project_id")
"{\"shotCount\":10,\"name\":\"sigma_x job\",\"machineID\":\"http://example.anyonsys.com\",\"billingaccountID\":\"project_id\",\"type\":\"circuit\",\"circuit\":{\"operations\":[{\"parameters\":{},\"type\":\"x\",\"qubits\":[0]}]}}"
Snowflurry.transpile
— Function.
transpile(::CompressSingleQubitGatesTranspiler, circuit::QuantumCircuit)::QuantumCircuit
Implementation of the CompressSingleQubitGatesTranspiler
transpiler stage which gathers all single-qubit gates sharing a common target in an input circuit and combines them into single Universal
gates in a new circuit. Gates ordering may differ when gates are applied to different qubits, but the result of the input and output circuit on any arbitrary state Ket
is unchanged (up to a global phase).
Examples
julia> transpiler = CompressSingleQubitGatesTranspiler();
julia> circuit = QuantumCircuit(qubit_count = 2, instructions = [sigma_x(1), sigma_y(1)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──X────Y──
q[2]:──────────
julia> transpiled_circuit=transpile(transpiler,circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──U(θ=0.0000,ϕ=3.1416,λ=0.0000)──
q[2]:─────────────────────────────────
julia> compare_circuits(circuit,transpiled_circuit)
true
julia> circuit = QuantumCircuit(qubit_count = 3, instructions = [sigma_x(1),sigma_y(1),control_x(2,3),phase_shift(1,π/3)])
Quantum Circuit Object:
qubit_count: 3
bit_count: 3
q[1]:──X────Y─────────P(1.0472)──
q[2]:────────────*───────────────
|
q[3]:────────────X───────────────
julia> transpiled_circuit=transpile(transpiler,circuit)
Quantum Circuit Object:
qubit_count: 3
bit_count: 3
q[1]:──U(θ=0.0000,ϕ=-2.0944,λ=0.0000)───────
q[2]:────────────────────────────────────*──
|
q[3]:────────────────────────────────────X──
julia> compare_circuits(circuit,transpiled_circuit)
true
transpile(::CastSwapToCZGateTranspiler, circuit::QuantumCircuit)::QuantumCircuit
Implementation of the CastSwapToCZGateTranspiler
transpiler stage which expands all Swap gates into CZ
gates and single-qubit gates. The result of the input and output circuit on any arbitrary state Ket
is unchanged (up to a global phase).
Examples
julia> transpiler = CastSwapToCZGateTranspiler();
julia> circuit = QuantumCircuit(qubit_count = 2, instructions = [swap(1, 2)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──☒──
|
q[2]:──☒──
julia> transpile(transpiler,circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:───────────*────Y_m90────────────*────Y_90─────────────*──────────
| | |
q[2]:──Y_m90────Z─────────────Y_90────Z────────────Y_m90────Z────Y_90──
transpile(::CastCXToCZGateTranspiler, circuit::QuantumCircuit)::QuantumCircuit
Implementation of the CastCXToCZGateTranspiler
transpiler stage which expands all CX
gates into CZ
and Hadamard
gates. The result of the input and output circuit on any arbitrary state Ket
is unchanged (up to a global phase).
Examples
julia> transpiler = CastCXToCZGateTranspiler();
julia> circuit = QuantumCircuit(qubit_count = 2, instructions = [control_x(1, 2)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──*──
|
q[2]:──X──
julia> transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:───────*───────
|
q[2]:──H────Z────H──
transpile(::CastISwapToCZGateTranspiler, circuit::QuantumCircuit)::QuantumCircuit
Implementation of the CastISwapToCZGateTranspiler
transpiler stage which expands all ISwap
gates into CZ
gates and single-qubit gates. The result of the input and output circuit on any arbitrary state Ket
is unchanged (up to a global phase).
Examples
julia> transpiler = CastISwapToCZGateTranspiler();
julia> circuit = QuantumCircuit(qubit_count = 2, instructions = [iswap(1, 2)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──x──
|
q[2]:──x──
julia> transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──Y_m90─────────────*────Y_90─────────────*────Y_90──────────
| |
q[2]:───────────X_m90────Z────────────X_m90────Z────────────X_90──
transpile(::CastToffoliToCXGateTranspiler, circuit::QuantumCircuit)::QuantumCircuit
Implementation of the CastToffoliToCXGateTranspiler
transpiler stage which expands all Toffoli gates into CX
gates and single-qubit gates. The result of the input and output circuit on any arbitrary state Ket
is unchanged (up to a global phase).
Examples
julia> transpiler = CastToffoliToCXGateTranspiler();
julia> circuit = QuantumCircuit(qubit_count = 3, instructions = [toffoli(1, 2, 3)])
Quantum Circuit Object:
qubit_count: 3
bit_count: 3
q[1]:──*──
|
q[2]:──*──
|
q[3]:──X──
julia> transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 3
bit_count: 3
q[1]:──────────────────*────────────────────*──────────────*─────────T──────────*──
| | | |
q[2]:───────*──────────|─────────*──────────|────T─────────X──────────────T†────X──
| | | |
q[3]:──H────X────T†────X────T────X────T†────X─────────T─────────H──────────────────
transpile(::CastToPhaseShiftAndHalfRotationXTranspiler, circuit::QuantumCircuit)::QuantumCircuit
Implementation of the CastToPhaseShiftAndHalfRotationXTranspiler
transpiler stage which converts all single-qubit gates in an input circuit and converts them into combinations of PhaseShift
and RotationX
with angle π/2 in an output circuit. For any gate in the input circuit, the number of gates in the output varies between zero and 5. The result of the input and output circuit on any arbitrary state Ket
is unchanged (up to a global phase).
Examples
julia> transpiler = CastToPhaseShiftAndHalfRotationXTranspiler();
julia> circuit = QuantumCircuit(qubit_count = 2, instructions = [sigma_x(1)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──X──
q[2]:─────
julia> transpiled_circuit = transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──Z────X_90────Z────X_m90──
q[2]:───────────────────────────
julia> circuit = QuantumCircuit(qubit_count = 2, instructions = [sigma_y(1)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──Y──
q[2]:─────
julia> transpiled_circuit = transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──X_90────Z────X_m90──
q[2]:──────────────────────
julia> compare_circuits(circuit, transpiled_circuit)
true
julia> circuit = QuantumCircuit(qubit_count = 2, instructions = [universal(1, 0., 0., 0.)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──U(θ=0.0000,ϕ=0.0000,λ=0.0000)──
q[2]:─────────────────────────────────
julia> transpiled_circuit = transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:
q[2]:
julia> compare_circuits(circuit, transpiled_circuit)
true
transpile(::CastUniversalToRzRxRzTranspiler, circuit::QuantumCircuit)::QuantumCircuit
Implementation of the CastUniversalToRzRxRzTranspiler
transpiler stage which finds Universal
gates in an input circuit and casts them into a sequence of PhaseShift
(P), RotationX
(Rx) and PhaseShift
(P) gates in a new circuit. The result of the input and output circuit on any arbitrary state Ket
is unchanged (up to a global phase).
Examples
julia> transpiler = CastUniversalToRzRxRzTranspiler();
julia> circuit = QuantumCircuit(qubit_count = 2, instructions = [universal(1, π/2, π/4, π/8)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──U(θ=1.5708,ϕ=0.7854,λ=0.3927)──
q[2]:─────────────────────────────────
julia> transpiled_circuit = transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──P(-1.1781)────Rx(1.5708)────P(2.3562)──
q[2]:─────────────────────────────────────────
julia> compare_circuits(circuit, transpiled_circuit)
true
julia> circuit = QuantumCircuit(qubit_count = 2, instructions = [universal(1, 0, π/4, 0)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──U(θ=0.0000,ϕ=0.7854,λ=0.0000)──
q[2]:─────────────────────────────────
julia> transpiled_circuit = transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──P(-1.5708)────Rx(0.0000)────P(2.3562)──
q[2]:─────────────────────────────────────────
julia> compare_circuits(circuit,transpiled_circuit)
true
transpile(::CastRxToRzAndHalfRotationXTranspiler, circuit::QuantumCircuit)::QuantumCircuit
Implementation of the CastRxToRzAndHalfRotationXTranspiler
transpiler stage which finds RotationX(θ)
gates in an input circuit and converts (casts) them into a sequence of gates: Z90
,X90
,PhaseShift(θ)
,XM90
,ZM90
in a new circuit. The result of the input and output circuit on any arbitrary state Ket
is unchanged (up to a global phase).
Examples
julia> transpiler=CastRxToRzAndHalfRotationXTranspiler();
julia> circuit = QuantumCircuit(qubit_count = 2, instructions = [rotation_x(1,π/8)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──Rx(0.3927)──
q[2]:──────────────
julia> transpiled_circuit = transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──Z_90────X_90────P(0.3927)────X_m90────Z_m90──
q[2]:───────────────────────────────────────────────
julia> compare_circuits(circuit, transpiled_circuit)
true
transpile(::SimplifyRxGatesTranspiler, circuit::QuantumCircuit)::QuantumCircuit
Implementation of the SimplifyRxGatesTranspiler
transpiler stage which finds RotationX
gates in an input circuit and according to its angle theta, casts them to one of the right-angle RotationX
gates, e.g., SigmaX
, X90
, or XM90
. In the case where theta≈0.
, the gate is removed. The result of the input and output circuit on any arbitrary state Ket
is unchanged (up to a global phase).
Examples
julia> transpiler = SimplifyRxGatesTranspiler();
julia> circuit = QuantumCircuit(qubit_count = 2, instructions = [rotation_x(1, pi/2)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──Rx(1.5708)──
q[2]:──────────────
julia> transpiled_circuit = transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──X_90──
q[2]:────────
julia> compare_circuits(circuit, transpiled_circuit)
true
julia> circuit = QuantumCircuit(qubit_count = 2, instructions = [rotation_x(1, pi)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──Rx(3.1416)──
q[2]:──────────────
julia> transpiled_circuit = transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──X──
q[2]:─────
julia> compare_circuits(circuit, transpiled_circuit)
true
julia> circuit = QuantumCircuit(qubit_count = 2, instructions = [rotation_x(1, 0.)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──Rx(0.0000)──
q[2]:──────────────
julia> transpiled_circuit = transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:
q[2]:
julia> compare_circuits(circuit, transpiled_circuit)
true
transpile(::SwapQubitsForAdjacencyTranspiler, circuit::QuantumCircuit)::QuantumCircuit
Implementation of the SwapQubitsForAdjacencyTranspiler
transpiler stage which adds Swap
gates around multi-qubit gates so that the final Operator
acts on adjacent qubits. The result of the input and output circuit on any arbitrary state Ket
is unchanged (up to a global phase).
Examples
julia> transpiler = SwapQubitsForAdjacencyTranspiler(LineConnectivity(6));
julia> circuit = QuantumCircuit(qubit_count = 6, instructions = [toffoli(4, 6, 1)])
Quantum Circuit Object:
qubit_count: 6
bit_count: 6
q[1]:──X──
|
q[2]:──|──
|
q[3]:──|──
|
q[4]:──*──
|
q[5]:──|──
|
q[6]:──*──
julia> transpiled_circuit = transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 6
bit_count: 6
q[1]:───────────────────────────X───────────────────────────
|
q[2]:───────☒───────────────────*───────────────────☒───────
| | |
q[3]:──☒────☒──────────────☒────*────☒──────────────☒────☒──
| | | |
q[4]:──☒──────────────☒────☒─────────☒────☒──────────────☒──
| |
q[5]:────────────☒────☒───────────────────☒────☒────────────
| |
q[6]:────────────☒─────────────────────────────☒────────────
julia> compare_circuits(circuit, transpiled_circuit)
true
transpile(::SimplifyRzGatesTranspiler, circuit::QuantumCircuit)::QuantumCircuit
Implementation of the SimplifyRzGatesTranspiler
transpiler stage which finds PhaseShift
gates in an input circuit and according to its phase angle phi, casts them to one of the right-angle RotationZ
gates, e.g., SigmaZ
, Z90
, ZM90
, Pi8
or Pi8Dagger
. In the case where phi≈0.
, the gate is removed. The result of the input and output circuit on any arbitrary state Ket
is unchanged (up to a global phase). The tolerance used for Base.isapprox()
in each case can be set by passing an optional argument to the Transpiler
, e.g: transpiler=SimplifyRzGatesTranspiler(1.0e-10)
Examples
julia> transpiler = SimplifyRzGatesTranspiler();
julia> circuit = QuantumCircuit(qubit_count = 2, instructions = [phase_shift(1, pi/2)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──P(1.5708)──
q[2]:─────────────
julia> transpiled_circuit = transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──Z_90──
q[2]:────────
julia> compare_circuits(circuit, transpiled_circuit)
true
julia> circuit = QuantumCircuit(qubit_count = 2, instructions = [phase_shift(1, pi)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──P(3.1416)──
q[2]:─────────────
julia> transpiled_circuit = transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──Z──
q[2]:─────
julia> compare_circuits(circuit, transpiled_circuit)
true
julia> circuit = QuantumCircuit(qubit_count = 2, instructions = [phase_shift(1, 0.)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──P(0.0000)──
q[2]:─────────────
julia> transpiled_circuit = transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:
q[2]:
julia> compare_circuits(circuit, transpiled_circuit)
true
transpile(::CompressRzGatesTranspiler, circuit::QuantumCircuit)::QuantumCircuit
Implementation of the CompressRzGatesTranspiler
transpiler stage which gathers all Rz-type gates sharing a common target in an input circuit and combines them into single PhaseShift gate in a new circuit. Gates ordering may differ when gates are applied to different qubits, but the result of the input and output circuit on any arbitrary state Ket
is unchanged (up to a global phase).
Examples
julia> transpiler = CompressRzGatesTranspiler();
julia> circuit = QuantumCircuit(qubit_count = 2, instructions = [sigma_z(1), z_90(1)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──Z────Z_90──
q[2]:─────────────
julia> transpiled_circuit = transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──P(-1.5708)──
q[2]:──────────────
julia> compare_circuits(circuit, transpiled_circuit)
true
julia> circuit = QuantumCircuit(qubit_count = 3, instructions = [sigma_z(1), pi_8(1), control_x(2,3), z_minus_90(1)])
Quantum Circuit Object:
qubit_count: 3
bit_count: 3
q[1]:──Z────T─────────Z_m90──
q[2]:────────────*───────────
|
q[3]:────────────X───────────
julia> transpiled_circuit = transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 3
bit_count: 3
q[1]:──P(2.3562)───────
q[2]:───────────────*──
|
q[3]:───────────────X──
julia> compare_circuits(circuit, transpiled_circuit)
true
transpile(::RemoveSwapBySwappingGatesTranspiler, circuit::QuantumCircuit)::QuantumCircuit
Removes the Swap
gates from the circuit
assuming all-to-all connectivity.
The initial state must be the ground state!
This transpiler stage assumes that the input state is $|0\rangle^{\otimes N}$ where $N$ is the number of qubits. The stage should not be used on sub-circuits where the input state is not $|0\rangle^{\otimes N}$.
This transpiler stage eliminates Swap
gates by moving the gates preceding each Swap
gate.
Examples
julia> transpiler = RemoveSwapBySwappingGatesTranspiler();
julia> circuit = QuantumCircuit(qubit_count = 2, instructions = [hadamard(1), swap(1, 2), sigma_x(2)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──H────☒───────
|
q[2]:───────☒────X──
julia> transpiled_circuit = transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──────────
q[2]:──H────X──
transpile(::SimplifyTrivialGatesTranspiler, circuit::QuantumCircuit)::QuantumCircuit
Implementation of the SimplifyTrivialGatesTranspiler
transpiler stage which finds gates which have no effect on the state Ket, such as Identity, and parameterized gates with null parameters such as rotation_x(target, 0.). The result of the input and output circuit on any arbitrary state Ket is unchanged (up to a global phase). The tolerance used for Base.isapprox() in each case can be set by passing an optional argument to the Transpiler, e.g: transpiler=SimplifyTrivialGatesTranspiler(1.0e-10)
Examples
julia> transpiler = SimplifyTrivialGatesTranspiler();
julia> circuit = QuantumCircuit(qubit_count = 2, instructions = [identity_gate(1)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──I──
q[2]:─────
julia> transpiled_circuit = transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:
q[2]:
julia> compare_circuits(circuit, transpiled_circuit)
true
julia> circuit = QuantumCircuit(qubit_count = 2, instructions = [phase_shift(1, 0.)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──P(0.0000)──
q[2]:─────────────
julia> transpiled_circuit = transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:
q[2]:
julia> compare_circuits(circuit, transpiled_circuit)
true
julia> circuit = QuantumCircuit(qubit_count = 2, instructions = [universal(1, 0., 0., 0.)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──U(θ=0.0000,ϕ=0.0000,λ=0.0000)──
q[2]:─────────────────────────────────
julia> transpiled_circuit = transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:
q[2]:
julia> compare_circuits(circuit, transpiled_circuit)
true
transpile(::ReadoutsAreFinalInstructionsTranspiler, circuit::QuantumCircuit)::QuantumCircuit
Ensures that each Readout
Instruction
is the last operation on each qubit where readouts are present, and that repeated readouts on the same qubit do not occur, or throws an error. It leaves the QuantumCircuit
unchanged.
Examples
julia> transpiler = ReadoutsAreFinalInstructionsTranspiler();
julia> circuit = QuantumCircuit(qubit_count=2, instructions = [hadamard(1), readout(1,1)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──H────✲──
q[2]:──────────
julia> transpiled_circuit = transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──H────✲──
q[2]:──────────
julia> circuit = QuantumCircuit(qubit_count=2, instructions = [hadamard(1), readout(1,1), sigma_x(1)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──H────✲────X──
q[2]:───────────────
julia> transpiled_circuit = transpile(transpiler, circuit)
ERROR: AssertionError: Cannot perform `Gate` following `Readout` on qubit: 1
[...]
julia> circuit = QuantumCircuit(qubit_count=2, instructions = [readout(1,1), readout(1,2)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──✲────✲──
q[2]:──────────
julia> transpiled_circuit = transpile(transpiler, circuit)
ERROR: AssertionError: Found multiple `Readouts` on qubit: 1
[...]
transpile(::CircuitContainsAReadoutTranspiler, circuit::QuantumCircuit)::QuantumCircuit
Ensures that at least one Readout
Instruction
is present on the QuantumCircuit
, or throws an error. It leaves the QuantumCircuit
unchanged.
Examples
julia> transpiler = CircuitContainsAReadoutTranspiler();
julia> circuit = QuantumCircuit(qubit_count=2, instructions = [hadamard(1), readout(1,1)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──H────✲──
q[2]:──────────
julia> transpiled_circuit = transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──H────✲──
q[2]:──────────
julia> circuit = QuantumCircuit(qubit_count=2, instructions = [hadamard(1)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──H──
q[2]:─────
julia> transpiled_circuit = transpile(transpiler, circuit)
ERROR: ArgumentError: QuantumCircuit is missing a `Readout`. Would not return any result.
[...]
transpile(::ReadoutsDoNotConflictTranspiler, circuit::QuantumCircuit)::QuantumCircuit
Ensures that each Readout
Instruction
present on the QuantumCircuit
do not have conflicting destination bit, or throws an error. It leaves the QuantumCircuit
unchanged.
Examples
julia> transpiler = ReadoutsDoNotConflictTranspiler();
julia> circuit = QuantumCircuit(qubit_count=2, instructions = [hadamard(1), readout(1,1)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──H────✲──
q[2]:──────────
julia> transpiled_circuit = transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──H────✲──
q[2]:──────────
julia> circuit = QuantumCircuit(qubit_count=2, instructions = [readout(1,1), readout(2,1)])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──✲───────
q[2]:───────✲──
julia> transpiled_circuit = transpile(transpiler, circuit)
ERROR: ArgumentError: `Readouts` in `QuantumCircuit` have conflicting destination bit: 1
[...]
transpile(::DecomposeSingleTargetSingleControlGatesTranspiler, circuit::QuantumCircuit)::QuantumCircuit
Implementation of the DecomposeSingleTargetSingleControlGatesTranspiler
transpiler stage which finds single-control, single-target Controlled
gates in an input circuit and casts them into a sequence of RotationZ
(Rz), ControlX
, Universal
(U) and PhaseShift
(P) gates in a new, equivalent circuit. For reference, see Nielsen and Chuang, "Quantum Computation and Quantum Information", p180. The result of the input and output circuit on any arbitrary state Ket
is unchanged (up to a global phase).
Note
If a global phase is applied by the kernel of the Controlled
gate on the target qubit, this decomposition preserves it.
For instance, a rotationz(pi) kernel and a phaseshift(pi) will yield results with a phase offset.
Examples
julia> transpiler = DecomposeSingleTargetSingleControlGatesTranspiler();
julia> circuit = QuantumCircuit(qubit_count = 2, instructions = [sigma_x(1), controlled(rotation_z(2, pi), [1])])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──X────────*───────
|
q[2]:───────Rz(3.1416)──
julia> transpiled_circuit = transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──X───────────────────*──────────────────────────────────────*───────────────────────────────────
| |
q[2]:───────Rz(-1.5708)────X────U(θ=0.0000,ϕ=-1.5708,λ=0.0000)────X────U(θ=0.0000,ϕ=3.1416,λ=0.0000)──
julia> compare_circuits(circuit, transpiled_circuit)
true
julia> simulate(transpiled_circuit)
4-element Ket{ComplexF64}:
0.0 + 0.0im
-0.0 + 0.0im
0.7071067811865477 - 0.7071067811865474im
0.0 + 0.0im
julia> circuit = QuantumCircuit(qubit_count = 2, instructions = [sigma_x(1), controlled(phase_shift(2, pi), [1])])
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──X────────*──────
|
q[2]:───────P(3.1416)──
julia> transpiled_circuit = transpile(transpiler, circuit)
Quantum Circuit Object:
qubit_count: 2
bit_count: 2
q[1]:──X───────────────────*──────────────────────────────────────*─────────────────────────────────────P(1.5708)──
| |
q[2]:───────Rz(-1.5708)────X────U(θ=0.0000,ϕ=-1.5708,λ=0.0000)────X────U(θ=0.0000,ϕ=3.1416,λ=0.0000)───────────────
julia> compare_circuits(circuit,transpiled_circuit)
true
julia> simulate(circuit)
4-element Ket{ComplexF64}:
0.0 + 0.0im
0.0 + 0.0im
1.0 + 0.0im
0.0 + 0.0im
Snowflurry.compare_circuits
— Function.
compare_circuits(c0::QuantumCircuit, c1::QuantumCircuit)::Bool
Tests for equivalence of two QuantumCircuit
based on their effect on an arbitrary input state (a Ket). QuantumCircuit
are equivalent if they both yield the same output for any input, up to a global phase. QuantumCircuit
with different ordering of gates that apply on different targets can also be equivalent.
Note
If there are Readouts
are present on either QuantumCircuit
, compare_circuits
checks that both circuits have readouts targeting the same qubits, and that no operations exist on those qubits following readouts.
Examples
julia> c0 = QuantumCircuit(qubit_count = 1, instructions = [sigma_x(1), sigma_y(1)])
Quantum Circuit Object:
qubit_count: 1
bit_count: 1
q[1]:──X────Y──
julia> c1 = QuantumCircuit(qubit_count = 1, instructions = [phase_shift(1, π)])
Quantum Circuit Object:
qubit_count: 1
bit_count: 1
q[1]:──P(3.1416)──
julia> compare_circuits(c0, c1)
true
julia> c0 = QuantumCircuit(qubit_count = 3, instructions = [sigma_x(1), sigma_y(1), control_x(2, 3)])
Quantum Circuit Object:
qubit_count: 3
bit_count: 3
q[1]:──X────Y───────
q[2]:────────────*──
|
q[3]:────────────X──
julia> c1 = QuantumCircuit(qubit_count = 3, instructions = [control_x(2, 3), sigma_x(1), sigma_y(1)])
Quantum Circuit Object:
qubit_count: 3
bit_count: 3
q[1]:───────X────Y──
q[2]:──*────────────
|
q[3]:──X────────────
julia> compare_circuits(c0, c1)
true
julia> c2 = QuantumCircuit(qubit_count = 3, instructions = [sigma_x(1), readout(1, 1)])
Quantum Circuit Object:
qubit_count: 3
bit_count: 3
q[1]:──X────✲──
q[2]:──────────
q[3]:──────────
julia> c3 = QuantumCircuit(qubit_count = 3, instructions = [sigma_x(1)])
Quantum Circuit Object:
qubit_count: 3
bit_count: 3
q[1]:──X──
q[2]:─────
q[3]:─────
julia> compare_circuits(c2,c3)
false
Snowflurry.circuit_contains_gate_type
— Function.
circuit_contains_gate_type(circuit::QuantumCircuit, gate_type::Type{<: AbstractGateSymbol})::Bool
Determines whether a type of gate is present in a circuit.
Examples
julia> circuit = QuantumCircuit(qubit_count = 1, instructions = [sigma_x(1), sigma_y(1)])
Quantum Circuit Object:
qubit_count: 1
bit_count: 1
q[1]:──X────Y──
julia> circuit_contains_gate_type(circuit, Snowflurry.SigmaX)
true
julia> circuit_contains_gate_type(circuit, Snowflurry.ControlZ)
false
Snowflurry.permute_qubits!
— Function.
permute_qubits!(circuit::QuantumCircuit,
qubit_mapping::AbstractDict{T,T}) where T<:Integer
Modifies a circuit
by moving the gates to other qubits based on a qubit_mapping
.
The dictionary qubit_mapping
contains key-value pairs describing how to update the target qubits. The key indicates which target qubit to change while the associated value specifies the new qubit. All the keys in the dictionary must also be present as values and vice versa.
For instance, Dict(1 => 2)
is not a valid qubit_mapping
, but Dict(1 => 2, 2 => 1)
is valid.
Examples
julia> c = QuantumCircuit(qubit_count = 3);
julia> push!(c, sigma_x(1), hadamard(2), sigma_y(3))
Quantum Circuit Object:
qubit_count: 3
bit_count: 3
q[1]:──X────────────
q[2]:───────H───────
q[3]:────────────Y──
julia> permute_qubits!(c, Dict(1 => 3, 3 => 1))
julia> show(c)
Quantum Circuit Object:
qubit_count: 3
bit_count: 3
q[1]:────────────Y──
q[2]:───────H───────
q[3]:──X────────────
Snowflurry.permute_qubits
— Function.
permute_qubits(circuit::QuantumCircuit,
qubit_mapping::AbstractDict{T,T})::QuantumCircuit where {T<:Integer}
Returns a QuantumCircuit
that is a copy of circuit
but where the gates have been moved to other qubits based on a qubit_mapping
.
The dictionary qubit_mapping
contains key-value pairs describing how to update the target qubits. The key indicates which target qubit to change while the associated value specifies the new qubit. All the keys in the dictionary must also be present as values and vice versa.
For instance, Dict(1=>2)
is not a valid qubit_mapping
, but Dict(1=>2, 2=>1)
is valid.
Examples
julia> c = QuantumCircuit(qubit_count = 3);
julia> push!(c, sigma_x(1), hadamard(2), sigma_y(3))
Quantum Circuit Object:
qubit_count: 3
bit_count: 3
q[1]:──X────────────
q[2]:───────H───────
q[3]:────────────Y──
julia> permute_qubits(c, Dict(1 => 3, 3 => 1))
Quantum Circuit Object:
qubit_count: 3
bit_count: 3
q[1]:────────────Y──
q[2]:───────H───────
q[3]:──X────────────