Quantum Objects
using PiccoloQuantumObjects
using SparseArrays # for visualization
⊗ = kron;
Quantum states and operators are represented as complex vectors and matrices. We provide a number of convenient ways to construct these objects. We also provide some tools for working with these objects, such as embedding operators in larger Hilbert spaces and selecting subspace indices.
Quantum states
We can construct quantum states from bitstrings or string representations. The string representations use atomic notation (ground state g
, excited state e
, etc.).
Ground state in a 2-level system.
ket_from_string("g", [2])
2-element Vector{ComplexF64}:
1.0 + 0.0im
0.0 + 0.0im
Superposition state coupled to a ground state in two 2-level systems.
ket_from_string("(g+e)g", [2,2])
4-element Vector{ComplexF64}:
0.7071067811865475 + 0.0im
0.0 + 0.0im
0.7071067811865475 + 0.0im
0.0 + 0.0im
|01⟩ in a 2-qubit system.
ket_from_bitstring("01")
4-element Vector{ComplexF64}:
0.0 + 0.0im
1.0 + 0.0im
0.0 + 0.0im
0.0 + 0.0im
Quantum operators
Frequently used operators are provided in PAULIS
and GATES
.
PiccoloQuantumObjects.Gates.GATES
— ConstantA constant dictionary GATES
containing common quantum gate matrices as complex-valued matrices. Each gate is represented by its unitary matrix.
GATES[:I]
- Identity: Leaves the state unchanged.GATES[:X]
- Pauli-X (NOT): Flips the qubit state.GATES[:Y]
- Pauli-Y: Rotates the qubit state around the Y-axis of the Bloch sphere.GATES[:Z]
- Pauli-Z: Flips the phase of the qubit state.GATES[:H]
- Hadamard: Creates superposition by transforming basis states.GATES[:CX]
- Controlled-X (CNOT): Flips the 2nd qubit (target) if the first qubit (control) is |1⟩.GATES[:CZ]
- Controlled-Z (CZ): Flips the phase of the 2nd qubit (target) if the 1st qubit (control) is |1⟩.GATES[:XI]
- Complex: A gate for complex operations.GATES[:sqrtiSWAP]
- Square root of iSWAP: Partially swaps two qubits with a phase.
Quantum operators can also be constructed from strings.
operator_from_string("X")
2×2 Matrix{ComplexF64}:
0.0+0.0im 1.0+0.0im
1.0+0.0im 0.0+0.0im
operator_from_string("XZ")
4×4 Matrix{ComplexF64}:
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
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
Annihilation and creation operators are provided for oscillator systems.
a = annihilate(3)
3×3 Matrix{ComplexF64}:
0.0+0.0im 1.0+0.0im 0.0+0.0im
0.0+0.0im 0.0+0.0im 1.41421+0.0im
0.0+0.0im 0.0+0.0im 0.0+0.0im
a⁺ = create(3)
3×3 Matrix{ComplexF64}:
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 1.41421-0.0im 0.0-0.0im
a'a
3×3 Matrix{ComplexF64}:
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 2.0+0.0im
Random operators
The haar_random
function draws random unitary operators according to the Haar measure.
haar_random(3)
3×3 Matrix{ComplexF64}:
0.322532+0.417251im 0.0934113+0.432683im -0.0488445+0.723567im
-0.0170277+0.186577im 0.73275-0.500004im -0.414894+0.0763901im
-0.790534+0.248657im 0.110747-0.0697756im 0.473358+0.268375im
If we want to generate random operations that are close to the identity, we can use the haar_identity
function.
haar_identity(2, 0.1)
2×2 Matrix{ComplexF64}:
0.991421-0.00439175im -0.130514-0.00552259im
0.130615-0.00203104im 0.99095+0.030898im
A smaller radius means the random operator is closer to the identity.
haar_identity(2, 0.01)
2×2 Matrix{ComplexF64}:
0.999998+0.00202651im 0.000189584+9.40408e-6im
-0.000189617+8.70744e-6im 0.999999+0.00164773im
Embedded operators
Sometimes we want to embed a quantum operator into a larger Hilbert space, $\mathcal{H}$, which we decompose into subspace and leakage components:
\[ \mathcal{H} = \mathcal{H}_{\text{subspace}} \oplus \mathcal{H}_{\text{leakage}},\]
In quantum computing, the computation is encoded in a subspace
, while the remaining leakage
states should be avoided.
The embed
and unembed
functions
The embed
function allows to embed a quantum operator in a larger Hilbert space.
PiccoloQuantumObjects.EmbeddedOperators.embed
— Functionembed(operator::AbstractMatrix{<:Number}, subspace::AbstractVector{Int}, levels::Int)
Embed an operator
in the subspace
of a larger matrix of size levels x levels
.
embed(subspace_operator::AbstractMatrix{<:Number}, embedded_operator::EmbeddedOperator)
Embed the subspace_operator
in the subspace of a larger embedded_operator
.
The unembed
function allows to unembed a quantum operator from a larger Hilbert space.
PiccoloQuantumObjects.EmbeddedOperators.unembed
— Functionunembed(matrix::AbstractMatrix{<:Number}, subspace::AbstractVector{Int})
Unembed a subspace operator from the matrix
. This is equivalent to calling matrix[subspace, subspace]
.
unembed(embedded_op::EmbeddedOperator)::Matrix{ComplexF64}
Unembed an embedded operator, returning the original operator.
unembed(op::AbstractMatrix, embedded_op::EmbeddedOperator)
Unembed a sub-matrix from the op
at the subspace defined by embedded_op
.
Embed a two-level X gate into a multilevel system.
levels = 3
X = GATES[:X]
subspace_indices = 1:2
X_embedded = embed(X, subspace_indices, levels)
3×3 Matrix{ComplexF64}:
0.0+0.0im 1.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
Unembed to retrieve the original operator.
X_original = unembed(X_embedded, subspace_indices)
2×2 Matrix{ComplexF64}:
0.0+0.0im 1.0+0.0im
1.0+0.0im 0.0+0.0im
The EmbeddedOperator
type
The EmbeddedOperator
type stores information about an operator embedded in the subspace of a larger quantum system.
PiccoloQuantumObjects.EmbeddedOperators.EmbeddedOperator
— TypeEmbeddedOperator
Embedded operator type to represent an operator embedded in a subspace of a larger quantum system.
Fields
operator::Matrix{ComplexF64}
: Embedded operator of sizeprod(subsystem_levels) x prod(subsystem_levels)
.subspace::Vector{Int}
: Indices of the subspace the operator is embedded in.subsystem_levels::Vector{Int}
: Levels of the subsystems in the composite system.
We construct an embedded operator in the same manner as the embed
function.
PiccoloQuantumObjects.EmbeddedOperators.EmbeddedOperator
— MethodEmbeddedOperator(subspace_operator::Matrix{<:Number}, subspace::AbstractVector{Int}, subsystem_levels::AbstractVector{Int})
Create an embedded operator. The operator
is embedded at the subspace
of the system spanned by the subsystem_levels
.
Embed an X gate in the first qubit's subspace within two 3-level systems.
gate = GATES[:X] ⊗ GATES[:I]
subsystem_levels = [3, 3]
subspace_indices = get_subspace_indices([1:2, 1:2], subsystem_levels)
embedded_operator = EmbeddedOperator(gate, subspace_indices, subsystem_levels)
EmbeddedOperator(ComplexF64[0.0 + 0.0im 0.0 + 0.0im … 0.0 + 0.0im 0.0 + 0.0im; 0.0 + 0.0im 0.0 + 0.0im … 0.0 + 0.0im 0.0 + 0.0im; … ; 0.0 + 0.0im 0.0 + 0.0im … 0.0 + 0.0im 0.0 + 0.0im; 0.0 + 0.0im 0.0 + 0.0im … 0.0 + 0.0im 0.0 + 0.0im], [1, 2, 4, 5], [3, 3])
Show the full operator.
embedded_operator.operator .|> real |> sparse
9×9 SparseArrays.SparseMatrixCSC{Float64, Int64} with 4 stored entries:
⋅ ⋅ ⋅ 1.0 ⋅ ⋅ ⋅ ⋅ ⋅
⋅ ⋅ ⋅ ⋅ 1.0 ⋅ ⋅ ⋅ ⋅
⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅
1.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅
⋅ 1.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅
⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅
⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅
⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅
⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅
Get the original operator back.
unembed(embedded_operator) .|> real |> sparse
4×4 SparseArrays.SparseMatrixCSC{Float64, Int64} with 4 stored entries:
⋅ ⋅ 1.0 ⋅
⋅ ⋅ ⋅ 1.0
1.0 ⋅ ⋅ ⋅
⋅ 1.0 ⋅ ⋅
Embedded operators for composite systems are also supported.
PiccoloQuantumObjects.EmbeddedOperators.EmbeddedOperator
— MethodEmbeddedOperator(
subspace_operator::AbstractMatrix{<:Number},
subsystem_indices::AbstractVector{Int},
subspaces::AbstractVector{<:AbstractVector{Int}},
subsystem_levels::AbstractVector{Int}
)
Embed the subspace_operator
into the provided subspaces
of a composite system, where the subsystem_indices
list the subspaces at which the operator is defined, and the subsystem_levels
list the levels of the subsystems in which the operator is embedded.
This is a two step process.
- The provided subspace operator is
lift
-ed from thesubsystem_indices
where it is defined into the space spanned by the composite system'ssubspaces
. - The lifted operator is embedded into the full Hilbert space spanned by the
subsystem_levels
.
Embed a CZ gate with control qubit 1 and target qubit 3 into a composite system made up of three 3-level systems. An identity is performed on qubit 2.
subsystem_levels = [3, 3, 3]
subspaces = [1:2, 1:2, 1:2]
embedded_operator = EmbeddedOperator(GATES[:CZ], [1, 3], subspaces, subsystem_levels)
unembed(embedded_operator) .|> real |> sparse
8×8 SparseArrays.SparseMatrixCSC{Float64, Int64} with 8 stored entries:
1.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅
⋅ 1.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅
⋅ ⋅ 1.0 ⋅ ⋅ ⋅ ⋅ ⋅
⋅ ⋅ ⋅ 1.0 ⋅ ⋅ ⋅ ⋅
⋅ ⋅ ⋅ ⋅ 1.0 ⋅ ⋅ ⋅
⋅ ⋅ ⋅ ⋅ ⋅ -1.0 ⋅ ⋅
⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 1.0 ⋅
⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ -1.0
Subspace and leakage indices
The get_subspace_indices
function
The get_subspace_indices
function is a convenient way to get the indices of a subspace in a larger quantum system.
PiccoloQuantumObjects.EmbeddedOperators.get_subspace_indices
— Functionget_subspace_indices(subspace::AbstractVector{Int}, levels::Int)
get_subspace_indices(subspaces::Vector{<:AbstractVector{Int}}, subsystem_levels::AbstractVector{Int})
get_subspace_indices(subsystem_levels::AbstractVector{Int}; subspace=1:2)
get_subspace_indices(op::EmbeddedOperator)
Get the indices for the provided subspace of the quantum system.
Its dual function is get_leakage_indices
.
get_subspace_indices(1:2, 5) |> collect, get_leakage_indices(1:2, 5) |> collect
([1, 2], [3, 4, 5])
Composite systems are supported. Get the indices of the two-qubit subspace within two 3-level systems.
get_subspace_indices([1:2, 1:2], [3, 3])
4-element Vector{Int64}:
1
2
4
5
Qubits are assumed if the indices are not provided.
get_subspace_indices([3, 3])
4-element Vector{Int64}:
1
2
4
5
get_leakage_indices([3, 3])
5-element Vector{Int64}:
3
6
7
8
9
Excitation number restrictions
Sometimes we want to cap the number of excitations we allow across a composite system. For example, if we want to restrict ourselves to the ground and single excitation states of two 3-level systems:
get_enr_subspace_indices(1, [3, 3])
3-element Vector{Int64}:
1
2
4
The get_iso_vec_subspace_indices
function
For isomorphic operators, the get_iso_vec_subspace_indices
function can be used to find the appropriate vector indices of the equivalent operator subspace. See also, Isomorphisms#Quantum-operator-isomorphisms.
PiccoloQuantumObjects.EmbeddedOperators.get_iso_vec_subspace_indices
— Functionget_iso_vec_subspace_indices(subspace::AbstractVector{Int}, subsystem_levels::AbstractVector{Int})
get_iso_vec_subspace_indices(op::EmbeddedOperator)
Get the indices for the subspace in the isomorphic vector space for operators.
Its dual function is get_iso_vec_leakage_indices
, which by default only returns the leakage indices of the blocks:
\[\mathcal{H}_{\text{subspace}} \otimes \mathcal{H}_{\text{subspace}},\quad \mathcal{H}_{\text{subspace}} \otimes \mathcal{H}_{\text{leakage}},\quad \mathcal{H}_{\text{leakage}} \otimes \mathcal{H}_{\text{subspace}}\]
allowing for leakage-suppressing code to disregard the uncoupled pure-leakage space.
get_iso_vec_subspace_indices(1:2, 3)
8-element Vector{Int64}:
1
2
4
5
7
8
10
11
without_pure_leakage = get_iso_vec_leakage_indices(1:2, 3)
8-element Vector{Int64}:
3
6
9
12
13
14
16
17
Show the pure-leakage indices.
with_pure_leakage = get_iso_vec_leakage_indices(1:2, 3, ignore_pure_leakage=false)
setdiff(with_pure_leakage, without_pure_leakage)
2-element Vector{Int64}:
15
18
The pure-leakage indices can grow quickly!
without_pure_leakage = get_iso_vec_leakage_indices([1:2, 1:2], [4, 4])
with_pure_leakage = get_iso_vec_leakage_indices([1:2, 1:2], [4, 4], ignore_pure_leakage=false)
setdiff(with_pure_leakage, without_pure_leakage) |> length
288
This page was generated using Literate.jl.