Quantum Registers
A quantum register is a quantum state or a batch of quantum states. Yao
provides two types of quantum registers ArrayReg
and BatchedArrayReg
.
YaoAPI.AbstractRegister
— TypeAbstractRegister{D}
Abstract type for quantum registers. Type parameter D
is the number of levels in each qudit. For qubits, D = 2
.
Required methods
Optional methods
Missing docstring for AbstractArrayReg
. Check Documenter's build log for details.
YaoArrayRegister.ArrayReg
— TypeArrayReg{D,T,MT<:AbstractMatrix{T}} <: AbstractArrayRegister{D}
ArrayReg{D}(raw)
ArrayReg(raw::AbstractVecOrMat; nlevel=2)
ArrayReg(r::ArrayReg)
Simulated full amplitude register type, it uses an array to represent corresponding one or a batch of quantum states. T
is the numerical type for each amplitude, it is ComplexF64
by default.
ArrayReg
constructor will not normalize the quantum state. If you need a normalized quantum state remember to use normalize!(register)
on the register or normalize the input raw array with normalize
or batched_normalize!
.
YaoArrayRegister.BatchedArrayReg
— TypeBatchedArrayReg{D,T,MT<:AbstractMatrix{T}} <: AbstractArrayReg{D}
BatchedArrayReg(raw, nbatch; nlevel=2)
BatchedArrayReg{D}(raw, nbatch)
Simulated batched full amplitude register type, it uses an array to represent corresponding one or a batch of quantum states. T
is the numerical type for each amplitude, it is ComplexF64
by default.
BatchedArrayReg
constructor will not normalize the quantum state. If you need a normalized quantum state remember to use normalize!(register)
on the register or normalize the input raw array with normalize
or batched_normalize!
.
We define some shortcuts to create simulated quantum states easier:
YaoArrayRegister.arrayreg
— Functionarrayreg(state; nbatch::Union{Integer,NoBatch}=NoBatch(), nlevel::Integer=2)
Create an array register, if nbatch is a integer, it will return a BatchedArrayReg
.
arrayreg([T=ComplexF64], bit_str; nbatch=NoBatch())
Construct an array register from bit string literal. For bit string literal please read @bit_str
.
Examples
julia> arrayreg(bit"1010")
ArrayReg{2, ComplexF64, Array...}
active qubits: 4/4
nlevel: 2
julia> arrayreg(ComplexF32, bit"1010")
ArrayReg{2, ComplexF32, Array...}
active qubits: 4/4
nlevel: 2
YaoArrayRegister.product_state
— Functionproduct_state([T=ComplexF64], dit_str; nbatch=NoBatch(), no_transpose_storage=false)
product_state([T=ComplexF64], nbits::Int, val::Int; nbatch=NoBatch(), nlevel=2, no_transpose_storage=false)
product_state([T=ComplexF64], vector; nbatch=NoBatch(), nlevel=2, no_transpose_storage=false)
Create an ArrayReg
of product state. The configuration can be specified with a dit string, which can be defined with @bit_str
or @dit_str
. Or equivalently, it can be specified explicitly with nbits
, val
and nlevel
. See also zero_state
, rand_state
, uniform_state
.
Examples
julia> reg = product_state(dit"120;3"; nbatch=2)
BatchedArrayReg{3, ComplexF64, Transpose...}
active qudits: 3/3
nlevel: 3
nbatch: 2
julia> measure(reg)
1×2 Matrix{BitBasis.DitStr64{3, 3}}:
120 ₍₃₎ 120 ₍₃₎
julia> product_state(bit"100"; nbatch=2);
julia> r1 = product_state(ComplexF32, bit"001"; nbatch=2);
julia> r2 = product_state(ComplexF32, [1, 0, 0]; nbatch=2);
julia> r3 = product_state(ComplexF32, 3, 0b001; nbatch=2);
julia> r1 ≈ r2 # because we read bit strings from right to left, vectors from left to right.
true
julia> r1 ≈ r3
true
YaoArrayRegister.zero_state
— Functionzero_state([T=ComplexF64], n::Int; nbatch::Int=NoBatch())
Create an AbstractArrayReg
that initialized to state $|0\rangle^{\otimes n}$. See also product_state
, rand_state
, uniform_state
and ghz_state
.
Examples
julia> zero_state(4)
ArrayReg{2, ComplexF64, Array...}
active qubits: 4/4
nlevel: 2
julia> zero_state(ComplexF32, 4)
ArrayReg{2, ComplexF32, Array...}
active qubits: 4/4
nlevel: 2
julia> zero_state(ComplexF32, 4; nbatch=3)
BatchedArrayReg{2, ComplexF32, Transpose...}
active qubits: 4/4
nlevel: 2
nbatch: 3
YaoArrayRegister.zero_state_like
— Functionzero_state_like(register, n) -> AbstractRegister
Create a register initialized to zero from an existing one.
Examples
julia> reg = rand_state(3; nbatch=2);
julia> zero_state_like(reg, 2)
BatchedArrayReg{2, ComplexF64, Array...}
active qubits: 2/2
nlevel: 2
nbatch: 2
YaoArrayRegister.rand_state
— Functionrand_state([T=ComplexF64], n::Int; nbatch=NoBatch(), no_transpose_storage=false)
Create a random AbstractArrayReg
with total number of qudits n
.
Examples
julia> rand_state(4)
ArrayReg{2, ComplexF64, Array...}
active qubits: 4/4
nlevel: 2
julia> rand_state(ComplexF64, 4)
ArrayReg{2, ComplexF64, Array...}
active qubits: 4/4
nlevel: 2
julia> rand_state(ComplexF64, 4; nbatch=2)
BatchedArrayReg{2, ComplexF64, Transpose...}
active qubits: 4/4
nlevel: 2
nbatch: 2
YaoArrayRegister.uniform_state
— Functionuniform_state([T=ComplexF64], n; nbatch=NoBatch(), no_transpose_storage=false)
Create a uniform state:
\[\frac{1}{\sqrt{2^n}} \sum_{k=0}^{2^{n}-1} |k\rangle.\]
This state can also be created by applying H
(Hadmard gate) on $|00⋯00⟩$ state.
Example
julia> uniform_state(4; nbatch=2)
BatchedArrayReg{2, ComplexF64, Transpose...}
active qubits: 4/4
nlevel: 2
nbatch: 2
julia> uniform_state(ComplexF32, 4; nbatch=2)
BatchedArrayReg{2, ComplexF32, Transpose...}
active qubits: 4/4
nlevel: 2
nbatch: 2
YaoArrayRegister.ghz_state
— Functionghz_state([T=ComplexF64], n::Int; nbatch::Int=NoBatch())
Create a GHZ state (or a cat state) that defined as
\[\frac{|0\rangle^{\otimes n} + |1\rangle^{\otimes n}}{\sqrt{2}}.\]
Examples
julia> ghz_state(4)
ArrayReg{2, ComplexF64, Array...}
active qubits: 4/4
nlevel: 2
YaoAPI.clone
— Functionclone(register, n)
Create an ArrayReg
by cloning the original register
for n
times on batch dimension. This function is only for emulation.
Example
julia> clone(arrayreg(bit"101"; nbatch=3), 4)
BatchedArrayReg{2, ComplexF64, Array...}
active qubits: 3/3
nlevel: 2
nbatch: 12
In a register, qubits are distinguished as active and inactive (or remaining). The total number of qubits is the number of active qubits plus the number of remaining qubits. Only active qubits are visible to quantum operators and the number of these qubits are the size of a register. Making this distinction of qubits allows writing reusable quantum circuits. For example, Suppose we want to run a quantum Fourier transformation circuit of size 4 on qubits (1, 3, 5, 7)
, we first set the target qubits to active qubits the reset to inactive, then we apply the circuit on it, finally we unset the inactive qubits.
YaoAPI.nqudits
— Functionnqudits(register) -> Int
Returns the total number of qudits in register
.
YaoAPI.nqubits
— Functionnqubits(register) -> Int
Returns the (total) number of qubits. See nactive
, nremain
for more details.
YaoAPI.nactive
— Functionnactive(register) -> Int
Returns the number of active qudits in register
. Here, active qudits means the system qubits that operators can be applied on.
YaoAPI.nremain
— Functionnremain(register) -> Int
Returns the number of inactive qudits in register
. It equals to subtracting nqudits
and nactive
.
Missing docstring for nbatch, nlevel, focus!
. Check Documenter's build log for details.
YaoAPI.focus
— Functionfocus(f, register, locs)
Call a callable f
under the context of focus
. See also focus!
.
Examples
To print the focused register
julia> r = arrayreg(bit"101100")
ArrayReg{2, ComplexF64, Array...}
active qubits: 6/6
nlevel: 2
julia> focus(x->(println(x);x), r, (1, 2));
ArrayReg{2, ComplexF64, Array...}
active qubits: 2/6
nlevel: 2
YaoAPI.relax!
— Functionrelax!(register[, locs]; to_nactive=nqudits(register)) -> register
relax!(locs::Int...; to_nactive=nqudits(register)) -> f(register) -> register
Inverse transformation of focus!
, where to_nactive
is the number of active bits for target register. If the register is not provided, returns a lambda function that takes a register as input.
Examples
julia> reg = product_state(bit"01101")
ArrayReg{2, ComplexF64, Array...}
active qubits: 5/5
nlevel: 2
julia> focus!(reg, (1,3,4))
ArrayReg{2, ComplexF64, Array...}
active qubits: 3/5
nlevel: 2
julia> relax!(reg, (1,3,4))
ArrayReg{2, ComplexF64, Array...}
active qubits: 5/5
nlevel: 2
Missing docstring for zero_state
. Check Documenter's build log for details.
YaoArrayRegister.exchange_sysenv
— Functionexchange_sysenv(reg::AbstractArrayReg) -> AbstractRegister
Exchange system (focused qubits) and environment (remaining qubits).
julia> reg = rand_state(5)
ArrayReg{2, ComplexF64, Array...}
active qubits: 5/5
nlevel: 2
julia> focus!(reg, (2,4))
ArrayReg{2, ComplexF64, Array...}
active qubits: 2/5
nlevel: 2
julia> exchange_sysenv(reg)
ArrayReg{2, ComplexF64, Adjoint...}
active qubits: 3/5
nlevel: 2
Storage
Both ArayReg
and BatchedArrayReg
use matrices as the storage. For example, for a quantum register with $a$ active qubits, $r$ remaining qubits and batch size $b$, the storage is as follows
The first dimension of size $2^a$ is for active qubits, only this subset of qubits are allowed to interact with blocks. Since we reshaped the state vector into a matrix, applying a quantum operator can always be represented as a matrix-matrix multiplication . In practice, most gates have in-place implementation that does not require constructing the operator matrix explicitly.
You can access different views of the storage of an ArrayReg
with the following functions:
YaoArrayRegister.state
— Functionstate(register::AbstractArrayReg) -> Matrix
Returns the raw array storage of register
. See also statevec
.
state(ρ::DensityMatrix) -> Matrix
Return the raw state of density matrix ρ
.
BitBasis.basis
— Functionbasis(register) -> UnitRange
Returns an UnitRange
of the all the bits in the Hilbert space of given register.
julia> collect(basis(rand_state(3)))
8-element Vector{DitStr{2, 3, Int64}}:
000 ₍₂₎
001 ₍₂₎
010 ₍₂₎
011 ₍₂₎
100 ₍₂₎
101 ₍₂₎
110 ₍₂₎
111 ₍₂₎
basis(ditstr) -> UnitRange{DitStr{D,N,T}}
basis(DitStr{D,N,T}) -> UnitRange{DitStr{D,N,T}}
Returns the UnitRange
for basis in Hilbert Space of qudits.
YaoArrayRegister.statevec
— Functionstatevec(r::ArrayReg) -> array
Return a state matrix/vector by droping the last dimension of size 1 (i.e. nactive(r) = nqudits(r)
). See also state
.
statevec
is not type stable. It may cause performance slow down.
YaoArrayRegister.relaxedvec
— Functionrelaxedvec(r::AbstractArrayReg) -> AbstractArray
Return a vector representation of state, with all qudits activated. See also state
, statevec
.
BitBasis.hypercubic
— Functionhypercubic(r::ArrayReg) -> AbstractArray
Return the hypercubic representation (high dimensional tensor) of this register, only active qudits are considered. See also rank3
and state
.
hypercubic(A::Array) -> Array
get the hypercubic representation for an array.
YaoArrayRegister.rank3
— Functionrank3(r::ArrayReg)
Return the rank 3 tensor representation of state, the 3 dimensions are (activated space, remaining space, batch dimension). See also hypercubic
and state
.
YaoAPI.viewbatch
— Functionviewbatch(register, i::Int) -> AbstractRegister
Returns the i
-th single register of a batched register. The returned instance is a view of the original register, i.e. inplace operation changes the original register directly.
Examples
julia> reg = zero_state(5; nbatch=2);
julia> apply!(viewbatch(reg, 2), put(5, 2=>X));
julia> measure(reg; nshots=3)
3×2 Matrix{DitStr{2, 5, Int64}}:
00000 ₍₂₎ 00010 ₍₂₎
00000 ₍₂₎ 00010 ₍₂₎
00000 ₍₂₎ 00010 ₍₂₎
YaoArrayRegister.transpose_storage
— Functiontranspose_storage(register) -> register
Transpose the register storage. Sometimes transposed storage provides better performance for batched simulation.
Operations
The list of arithmetic operations for ArrayReg
include
+
-
*
/
(scalar)adjoint
Then the inner product can be computed as follows.
julia> reg = rand_state(3);
julia> reg' * reg
0.9999999999999998 + 0.0im
Missing docstring for AdjointArrayReg
. Check Documenter's build log for details.
We also have some faster inplace versions of arithematic operations
Missing docstring for regadd!, regsub!, regscale!,
. Check Documenter's build log for details.
We also define the following functions for state normalization, and distance measurement.
LinearAlgebra.normalize!
— Functionnormalize!(r::AbstractArrayReg)
Normalize the register r
by its 2-norm. It changes the register directly.
Examples
The following code creates a normalized GHZ state.
julia> reg = product_state(bit"000") + product_state(bit"111");
julia> norm(reg)
1.4142135623730951
julia> isnormalized(reg)
false
julia> normalize!(reg);
julia> isnormalized(reg)
true
YaoArrayRegister.isnormalized
— Functionisnormalized(r::ArrayReg) -> Bool
Returns true if the register r
is normalized.
YaoAPI.fidelity
— Functionfidelity(register1, register2) -> Real/Vector{<:Real}
fidelity'(pair_or_reg1, pair_or_reg2) -> (g1, g2)
Return the fidelity between two states. Calcuate the fidelity between r1
and r2
, if r1
or r2
is not pure state (nactive(r) != nqudits(r)
), the fidelity is calcuated by purification. See also pure_state_fidelity
, purification_fidelity
.
Obtain the gradient with respect to registers and circuit parameters. For pair input ψ=>circuit
, the returned gradient is a pair of gψ=>gparams
, with gψ
the gradient of input state and gparams
the gradients of circuit parameters. For register input, the return value is a register.
Definition
The fidelity of two quantum state for qudits is defined as:
\[F(ρ, σ) = tr(\sqrt{\sqrt{ρ}σ\sqrt{ρ}})\]
This definition is different from the one in Wiki by a square.
Examples
julia> reg1 = uniform_state(3);
julia> reg2 = zero_state(3);
julia> fidelity(reg1, reg2)
0.35355339059327373
References
- Jozsa R. Fidelity for mixed quantum states[J]. Journal of modern optics, 1994, 41(12): 2315-2323.
- Nielsen M A, Chuang I. Quantum computation and quantum information[J]. 2002.
The original definition of fidelity $F$ was from "transition probability", defined by Jozsa in 1994, it is the square of what we use here.
YaoAPI.tracedist
— Functiontracedist(register1, register2)
Return the trace distance of register1
and register2
.
Definition
Trace distance is defined as following:
\[\frac{1}{2} || A - B ||_{\rm tr}\]
Examples
julia> reg1 = uniform_state(3);
julia> reg2 = zero_state(3);
julia> tracedist(reg1, reg2)
1.8708286933869704
References
- https://en.wikipedia.org/wiki/Trace_distance
Resource management and addressing
Missing docstring for add_qudits!
. Check Documenter's build log for details.
Missing docstring for add_qubits!
. Check Documenter's build log for details.
YaoAPI.append_qudits!
— Functionappend_qudits!(register, n::Int) -> register
append_qudits!(n::Int) -> λ(register)
Add n
qudits to given register in state |0>. i.e. |psi> -> |000> ⊗ |psi>, increased bits have higher indices.
If only an integer is provided, then returns a lambda function.
Examples
julia> reg = product_state(bit"01101")
ArrayReg{2, ComplexF64, Array...}
active qubits: 5/5
nlevel: 2
julia> append_qudits!(reg, 2)
ArrayReg{2, ComplexF64, Array...}
active qubits: 7/7
nlevel: 2
julia> measure(reg; nshots=3)
3-element Vector{DitStr{2, 7, Int64}}:
0001101 ₍₂₎
0001101 ₍₂₎
0001101 ₍₂₎
Note here, we read the bit string from right to left.
YaoAPI.append_qubits!
— Functionappend_qubits!(register, n::Int) -> register
append_qubits!(n::Int) -> λ(register)
Add n
qudits to given register in state |0>. It is an alias of append_qudits!
function.
YaoAPI.reorder!
— Functionreorder!(reigster, orders)
Reorder the locations of register by input orders. For a 3-qubit register, an order (i, j, k)
specifies the following reordering of qubits
- move the first qubit go to
i
, - move the second qubit go to
j
, - move the third qubit go to
k
.
The convention of reorder!
is different from the permutedims
function, one can use the sortperm
function to relate the permutation order and the order in this function.
Examples
julia> reg = product_state(bit"010101");
julia> reorder!(reg, (1,4,2,5,3,6));
julia> measure(reg)
1-element Vector{DitStr{2, 6, Int64}}:
000111 ₍₂₎
YaoAPI.invorder!
— Functioninvorder!(register)
Inverse the locations of the register.
Examples
julia> reg = product_state(bit"010101")
ArrayReg{2, ComplexF64, Array...}
active qubits: 6/6
nlevel: 2
julia> measure(invorder!(reg); nshots=3)
3-element Vector{DitStr{2, 6, Int64}}:
101010 ₍₂₎
101010 ₍₂₎
101010 ₍₂₎
Only a subset of qubits that does not interact with other qubits can be removed, the best approach is first measuring it in computational basis first. It can be done with the measure!
function by setting the first argument to RemoveMeasured()
.
Instruction set
Although we have matrix representation for Yao blocks, specialized instructions are much faster and memory efficient than using the matrix-matrix product. These instructions are specified with the instruct!
function listed bellow.
YaoAPI.instruct!
— Functioninstruct!([nlevel=Val(2), ]state, operator, locs[, control_locs, control_configs, theta])
Unified interface for applying an operator to a quantum state. It modifies the state
directly.
Arguments
nlevel
is the number of levels in each qudit,state
is a vector or matrix representing the quantum state, where the first dimension is the active qubit dimension, the second is the batch dimension.operator
is a quantum operator, which can beVal(GATE_SYMBOL)
or a matrix.locs::Tuple
is a tuple for specifying the locations this gate applied.control_locs::Tuple
andcontrol_configs
are tuples for specifying the control locations and control values.theta::Real
is the parameter for the gate, e.g.Val(:Rx)
gate takes a real number of its parameter.
Measurement
We have a true measure function measure!
that collapses the state after the measurement. We also have some "cheating" functions to facilitate classical simulation.
YaoAPI.measure!
— Functionmeasure!([postprocess,] [operator, ]register[, locs]; rng=Random.GLOBAL_RNG)
Measure current active qudits or qudits at locs
. If the operator is not provided, it will measure on the computational basis and collapse to a product state. Otherwise, the quantum state collapse to the subspace corresponds to the resulting eigenvalue of the observable.
Arguments
postprocess
is the postprocessing method, it can beNoPostProcess()
(default).ResetTo(config)
, reset to result state toconfig
. It can not be used ifoperator
is provided, because measuring an operator in general does not return a product state.RemoveMeasured()
, remove the measured qudits from the register. It is also incompatible with theoperator
argument.
operator::AbstractBlock
is the operator to measure.register::AbstractRegister
is the quantum state.locs
is the qubits to performance the measurement. Iflocs
is not provided, all current active qudits are measured (regarding to active qudits,
Keyword arguments
rng
is the random number generator.
Examples
The following example measures a random state on the computational basis and reset it to a certain bitstring value.
julia> reg = rand_state(3);
julia> measure!(ResetTo(bit"011"), reg)
110 ₍₂₎
julia> measure(reg; nshots=3)
3-element Vector{DitStr{2, 3, Int64}}:
011 ₍₂₎
011 ₍₂₎
011 ₍₂₎
julia> measure!(RemoveMeasured(), reg, (1,2))
11 ₍₂₎
julia> reg # removed qubits are not usable anymore
ArrayReg{2, ComplexF64, Array...}
active qubits: 1/1
nlevel: 2
Measuring an operator will project the state to the subspace associated with the returned eigenvalue.
julia> reg = uniform_state(3)
ArrayReg{2, ComplexF64, Array...}
active qubits: 3/3
nlevel: 2
julia> print_table(reg)
000 ₍₂₎ 0.35355 + 0.0im
001 ₍₂₎ 0.35355 + 0.0im
010 ₍₂₎ 0.35355 + 0.0im
011 ₍₂₎ 0.35355 + 0.0im
100 ₍₂₎ 0.35355 + 0.0im
101 ₍₂₎ 0.35355 + 0.0im
110 ₍₂₎ 0.35355 + 0.0im
111 ₍₂₎ 0.35355 + 0.0im
julia> measure!(repeat(3, Z, 1:3), reg)
-1.0 + 0.0im
julia> print_table(reg)
000 ₍₂₎ 0.0 + 0.0im
001 ₍₂₎ 0.5 + 0.0im
010 ₍₂₎ 0.5 + 0.0im
011 ₍₂₎ 0.0 + 0.0im
100 ₍₂₎ 0.5 + 0.0im
101 ₍₂₎ 0.0 + 0.0im
110 ₍₂₎ 0.0 + 0.0im
111 ₍₂₎ 0.5 + 0.0im
Here, we measured the parity operator, as a result, the resulting state collapsed to the subspace with either even or odd parity.
YaoAPI.measure
— Functionmeasure([, operator], register[, locs]; nshots=1, rng=Random.GLOBAL_RNG) -> Vector{Int}
Measure a quantum state and return measurement results of qudits. This measurement function a cheating version of measure!
that does not collapse the input state. It also does not need to recompute the quantum state for performing multiple shots measurement.
Arguments
operator::AbstractBlock
is the operator to measure.register::AbstractRegister
is the quantum state.locs
is the qubits to performance the measurement. Iflocs
is not provided, all current active qudits are measured (regarding to active qudits,
Keyword arguments
nshots::Int
is the number of shots.rng
is the random number generator.
Examples
julia> reg = product_state(bit"110")
ArrayReg{2, ComplexF64, Array...}
active qubits: 3/3
nlevel: 2
julia> measure(reg; nshots=3)
3-element Vector{DitStr{2, 3, Int64}}:
110 ₍₂₎
110 ₍₂₎
110 ₍₂₎
julia> measure(reg, (2,3); nshots=3)
3-element Vector{DitStr{2, 2, Int64}}:
11 ₍₂₎
11 ₍₂₎
11 ₍₂₎
The following example switches to the X basis for measurement.
julia> reg = apply!(product_state(bit"100"), repeat(3, H, 1:3))
ArrayReg{2, ComplexF64, Array...}
active qubits: 3/3
nlevel: 2
julia> measure(repeat(3, X, 1:3), reg; nshots=3)
3-element Vector{ComplexF64}:
-1.0 + 0.0im
-1.0 + 0.0im
-1.0 + 0.0im
julia> reg = apply!(product_state(bit"101"), repeat(3, H, 1:3))
ArrayReg{2, ComplexF64, Array...}
active qubits: 3/3
nlevel: 2
julia> measure(repeat(3, X, 1:3), reg; nshots=3)
3-element Vector{ComplexF64}:
1.0 - 0.0im
1.0 - 0.0im
1.0 - 0.0im
YaoAPI.select!
— Functionselect!(dest::AbstractRegister, src::AbstractRegister, bits::Integer...) -> AbstractRegister
select!(register::AbstractRegister, bits::Integer...) -> register
select!(b::Integer) -> f(register)
select a subspace of given quantum state based on input eigen state bits
. See also select
for the non-inplace version. If the register is not provided, it returns a lambda expression that takes a register as the input.
Examples
julia> reg = ghz_state(3)
ArrayReg{2, ComplexF64, Array...}
active qubits: 3/3
nlevel: 2
julia> select!(reg, bit"111")
ArrayReg{2, ComplexF64, Array...}
active qubits: 0/0
nlevel: 2
julia> norm(reg)
0.7071067811865476
The selection only works on the activated qubits, for example
julia> reg = focus!(ghz_state(3), (1, 2))
ArrayReg{2, ComplexF64, Array...}
active qubits: 2/3
nlevel: 2
julia> select!(reg, bit"11")
ArrayReg{2, ComplexF64, Array...}
active qubits: 0/1
nlevel: 2
julia> statevec(reg)
1×2 Matrix{ComplexF64}:
0.0+0.0im 0.707107+0.0im
Developers should overload select!(r::RegisterType, bits::NTuple{N, <:Integer})
and do not assume bits
has specific number of bits (e.g Int64
), or it will restrict the its maximum available number of qudits.
YaoAPI.select
— Functionselect(register, bits) -> AbstractRegister
The non-inplace version of select!
.
YaoAPI.collapseto!
— Functioncollapseto!(register, config)
Set the register
to bit string literal bit_str
(or an equivalent integer). About bit string literal, see more in @bit_str
. This interface is only for emulation.
Examples
The following code collapse a random state to a certain state.
julia> measure(collapseto!(rand_state(3), bit"001"); nshots=3)
3-element Vector{DitStr{2, 3, Int64}}:
001 ₍₂₎
001 ₍₂₎
001 ₍₂₎
YaoAPI.probs
— Functionprobs(register) -> Vector
Returns the probability distribution of computation basis, aka $|<x|ψ>|^2$.
Examples
julia> reg = product_state(bit"101");
julia> reg |> probs
8-element Vector{Float64}:
0.0
0.0
0.0
0.0
0.0
1.0
0.0
0.0
Missing docstring for most_probable,
. Check Documenter's build log for details.
Density matrices
YaoAPI.DensityMatrix
— TypeDensityMatrix{D,T,MT<:AbstractMatrix{T}} <: AbstractRegister{D}
DensityMatrix{D}(state::AbstractMatrix)
DensityMatrix(state::AbstractMatrix; nlevel=2)
Density matrix type, where state
is a matrix. Type parameter D
is the number of levels, it can also be specified by a keyword argument nlevel
.
YaoAPI.density_matrix
— Functiondensity_matrix(register_or_rho[, locations])
Returns the reduced density matrix for qubits at locations
(default: all qubits).
Examples
The following code gets the single site reduce density matrix for the GHZ state.
julia> reg = ghz_state(3)
ArrayReg{2, ComplexF64, Array...}
active qubits: 3/3
nlevel: 2
julia> density_matrix(reg, (2,)).state
2×2 Matrix{ComplexF64}:
0.5+0.0im 0.0+0.0im
0.0-0.0im 0.5+0.0im
YaoArrayRegister.rand_density_matrix
— Functionrand_density_matrix([T=ComplexF64], n::Int; nlevel::Int=2, pure::Bool=false)
Generate a random density matrix by partial tracing half of the pure state.
The generated density matrix is not strict hermitian due to rounding error. If you need to check hermicity, do not use ishermitian
consider using isapprox(dm.state, dm.state')
or explicit mark it as Hermitian
.
YaoArrayRegister.completely_mixed_state
— Functioncompletely_mixed_state([T=ComplexF64], n::Int; nlevel::Int=2)
Generate the completely mixed state with density matrix I(n) ./ nlevel^n
.
YaoAPI.partial_tr
— Functionpartial_tr(ρ, locs) -> DensityMatrix
Return a density matrix which is the partial traced on locs
.
YaoAPI.purify
— Functionpurify(r::DensityMatrix; nbit_env::Int=nactive(r)) -> ArrayReg
Get a purification of target density matrix.
Examples
The following example shows how to measure a local operator on the register, reduced density matrix and the purified register. Their results should be consistent.
julia> reg = ghz_state(3)
ArrayReg{2, ComplexF64, Array...}
active qubits: 3/3
nlevel: 2
julia> r = density_matrix(reg, (2,));
julia> preg = purify(r)
ArrayReg{2, ComplexF64, Array...}
active qubits: 1/2
nlevel: 2
julia> isapprox(expect(Z + Y, preg), 0.0; atol=1e-10)
true
julia> isapprox(expect(Z + Y, r), 0.0; atol=1e-10)
true
julia> isapprox(expect(put(3, 2=>(Z + Y)), reg), 0.0; atol=1e-10)
true
Missing docstring for von_neumann_entropy, mutual_information,
. Check Documenter's build log for details.