Full API
States
Stabilizer states can be represented with the Stabilizer
, Destabilizer
, MixedStabilizer
, and MixedDestabilizer
tableau data structures. You probably want to use MixedDestabilizer
which supports the widest set of operations.
Moreover, a MixedDestabilizer
can be stored inside a Register
together with a set of classical bits in which measurement results can be written.
Lastly, for Pauli frame simulations there is the PauliFrame
type, a tableau in which each row represents a different Pauli frame.
There are convenience constructors for common types of states and operators.
Operations
Acting on quantum states can be performed either:
- In a "linear algebra" language where unitaries, measurements, and other operations have separate interfaces. This is an explicitly deterministic lower-level interface, which provides a great deal of control over how tableaux are manipulated. See the Stabilizer Tableau Algebra Manual as a primer on these approaches.
- Or in a "circuit" language, where the operators (and measurements and noise) are represented as circuit gates. This is a higher-level interface in which the outcome of an operation can be stochastic. The API for it is centered around the
apply!
function. Particularly useful for Monte Carlo simulations and Perturbative Expansion Symbolic Results.
See the full list of operations for a list of implemented operations.
Autogenerated API list
QuantumClifford.QuantumClifford
— ModuleA module for using the Stabilizer formalism and simulating Clifford circuits.
QuantumClifford.continue_stat
— ConstantReturned by applywstatus!
if the circuit simulation should continue.
QuantumClifford.failure_stat
— ConstantReturned by applywstatus!
if the circuit reports a failure.
See also: VerifyOp
, BellMeasurement
.
QuantumClifford.false_success_stat
— ConstantReturned by applywstatus!
if the circuit reports a success, but it is a false positive (i.e., there was an undetected error).
See also: VerifyOp
, BellMeasurement
.
QuantumClifford.true_success_stat
— ConstantReturned by applywstatus!
if the circuit reports a success and there is no undetected error.
See also: VerifyOp
, BellMeasurement
.
QuantumClifford.AbstractSingleQubitOperator
— TypeSupertype of all single-qubit symbolic operators.
QuantumClifford.AbstractSymbolicOperator
— TypeSupertype of all symbolic operators. Subtype of AbstractCliffordOperator
QuantumClifford.AbstractTwoQubitOperator
— TypeSupertype of all two-qubit symbolic operators.
QuantumClifford.BellMeasurement
— TypeA Bell measurement performing the correlation measurement corresponding to the given pauli
projections on the qubits at the selected indices.
QuantumClifford.CircuitStatus
— TypeA convenience struct to represent the status of a circuit simulated by mctrajectories
QuantumClifford.ClassicalXOR
— TypeApplies an XOR gate to classical bits. Currently only implemented for functionality with pauli frames.
QuantumClifford.CliffordOperator
— TypeClifford Operator specified by the mapping of the basis generators.
julia> tCNOT
+X₁ ⟼ + XX
+X₂ ⟼ + _X
+Z₁ ⟼ + Z_
+Z₂ ⟼ + ZZ
+
+julia> phase_gate = C"Y
+ Z"
+X₁ ⟼ + Y
+Z₁ ⟼ + Z
+
+julia> stab = S"XI
+ IZ";
+
+
+julia> entangled = tCNOT*stab
++ XX
++ ZZ
+
+julia> CliffordOperator(T"YY")
+ERROR: DimensionMismatch: Input tableau should be of size 2n×n (top half is the X mappings and the bottom half are the Z mappings).
+[...]
Destabilizer
can also be converted.
julia> d = Destabilizer(S"Y")
+𝒟ℯ𝓈𝓉𝒶𝒷
++ Z
+𝒮𝓉𝒶𝒷
++ Y
+
+julia> CliffordOperator(d)
+X₁ ⟼ + Z
+Z₁ ⟼ + Y
QuantumClifford.Destabilizer
— TypeA tableau representation of a pure stabilizer state. The tableau tracks the destabilizers as well, for efficient projections. On initialization there are no checks that the provided state is indeed pure. This enables the use of this data structure for mixed stabilizer state, but a better choice would be to use MixedDestabilizer
.
QuantumClifford.MixedDestabilizer
— TypeA tableau representation for mixed stabilizer states that keeps track of the destabilizers in order to provide efficient projection operations.
The rank r
of the n
-qubit tableau is tracked, either so that it can be used to represent a mixed stabilizer state, or so that it can be used to represent an n-r
logical-qubit code over n
physical qubits. The "logical" operators are tracked as well.
When the constructor is called on an incomplete Stabilizer
it automatically calculates the destabilizers and logical operators, following chapter 4 of (Gottesman, 1997). Under the hood the conversion uses the canonicalize_gott!
canonicalization. That canonicalization permutes the columns of the tableau, but we automatically undo the column permutation in the preparation of a MixedDestabilizer
so that qubits are not reindexed. The boolean keyword arguments undoperm
and reportperm
can be used to control this behavior and to report the permutations explicitly.
See also: stabilizerview
, destabilizerview
, logicalxview
, logicalzview
QuantumClifford.MixedStabilizer
— TypeA slight improvement of the Stabilizer
data structure that enables more naturally and completely the treatment of mixed states, in particular when the project!
function is used.
QuantumClifford.NoiseOp
— TypeAn operator that applies the given noise
model to the qubits at the selected indices
.
QuantumClifford.NoiseOpAll
— TypeAn operator that applies the given noise
model to all qubits.
QuantumClifford.NoisyGate
— TypeA gate consisting of the given noise
applied after the given perfect Clifford gate
.
QuantumClifford.PauliChannel
— TypeA Pauli channel datastructure, mainly for use with StabMixture
See also: UnitaryPauliChannel
QuantumClifford.PauliFrame
— Typestruct PauliFrame{T, S} <: QuantumClifford.AbstractQCState
This is a wrapper around a tableau. This "frame" tableau is not to be viewed as a normal stabilizer tableau, although it does conjugate the same under Clifford operations. Each row in the tableau refers to a single frame. The row represents the Pauli operation by which the frame and the reference differ.
QuantumClifford.PauliFrame
— MethodPauliFrame(
+ frames,
+ qubits,
+ measurements
+) -> PauliFrame{Stabilizer{QuantumClifford.Tableau{Vector{UInt8}, LinearAlgebra.Adjoint{UInt64, Matrix{UInt64}}}}}
+
Prepare an empty set of Pauli frames with the given number of frames
and qubits
. Preallocates spaces for measurement
number of measurements.
QuantumClifford.PauliMeasurement
— TypeA Stabilizer measurement on the entirety of the quantum register.
projectrand!(state, pauli)
and apply!(state, PauliMeasurement(pauli))
give the same (possibly non-deterministic) result. Particularly useful when acting on Register
.
See also: apply!
, projectrand!
.
QuantumClifford.PauliNoise
— TypePauli noise model with probabilities px
, py
, and pz
respectively for the three types of Pauli errors.
QuantumClifford.PauliNoise
— MethodConstructs an unbiased Pauli noise model with total probability of error p
.
QuantumClifford.PauliOperator
— TypeA multi-qubit Pauli operator ($±\{1,i\}\{I,Z,X,Y\}^{\otimes n}$).
A Pauli can be constructed with the P
custom string macro or by building up one through products and tensor products of smaller operators.
julia> pauli3 = P"-iXYZ"
+-iXYZ
+
+julia> pauli4 = 1im * pauli3 ⊗ X
++ XYZX
+
+julia> Z*X
++iY
We use a typical F(2,2) encoding internally. The X and Z bits are stored in a single concatenated padded array of UInt chunks of a bit array.
julia> p = P"-IZXY";
+
+
+julia> p.xz
+2-element Vector{UInt64}:
+ 0x000000000000000c
+ 0x000000000000000a
You can access the X and Z bits through getters and setters or through the xview
, zview
, xbit
, and zbit
functions.
julia> p = P"XYZ"; p[1]
+(true, false)
+
+julia> p[1] = (true, true); p
++ YYZ
QuantumClifford.Register
— TypeA register, representing the state of a computer including both a tableaux and an array of classical bits (e.g. for storing measurement results)
QuantumClifford.Reset
— TypeReset the specified qubits to the given state.
Be careful, this operation implies first tracing out the qubits, which can lead to mixed states if these qubits were entangled with the rest of the system.
See also: sMRZ
QuantumClifford.SingleQubitOperator
— TypeA "symbolic" general single-qubit operator which permits faster multiplication than an operator expressed as an explicit tableau.
julia> op = SingleQubitOperator(2, true, true, true, false, true, true) # Tableau components and phases
+SingleQubitOperator on qubit 2
+X₁ ⟼ - Y
+Z₁ ⟼ - X
+
+julia> typeof(op)
+SingleQubitOperator
+
+julia> t_op = CliffordOperator(op, 3) # Transforming it back into an explicit tableau representation (specifying the size)
+X₁ ⟼ + X__
+X₂ ⟼ - _Y_
+X₃ ⟼ + __X
+Z₁ ⟼ + Z__
+Z₂ ⟼ - _X_
+Z₃ ⟼ + __Z
+
+julia> typeof(t_op)
+CliffordOperator{QuantumClifford.Tableau{Vector{UInt8}, Matrix{UInt64}}}
+
+julia> CliffordOperator(op, 1, compact=true) # You can also extract just the non-trivial part of the tableau
+X₁ ⟼ - Y
+Z₁ ⟼ - X
See also: sHadamard
, sPhase
, sId1
, sX
, sY
, sZ
, CliffordOperator
Or simply consult subtypes(QuantumClifford.AbstractSingleQubitOperator)
and subtypes(QuantumClifford.AbstractTwoQubitOperator)
for a full list. You can think of the s
prefix as "symbolic" or "sparse".
QuantumClifford.SparseGate
— TypeA Clifford gate, applying the given cliff
operator to the qubits at the selected indices
.
apply!(state, cliff, indices)
and apply!(state, SparseGate(cliff, indices))
give the same result.
QuantumClifford.StabMixture
— Typemutable struct StabMixture{T, F}
Represents mixture ∑ ϕᵢⱼ Pᵢ ρ Pⱼ† where ρ is a pure stabilizer state.
julia> StabMixture(S"-X")
+A mixture ∑ ϕᵢⱼ Pᵢ ρ Pⱼ† where ρ is
+𝒟ℯ𝓈𝓉𝒶𝒷
++ Z
+𝒮𝓉𝒶𝒷
+- X
+with ϕᵢⱼ | Pᵢ | Pⱼ:
+ 1.0+0.0im | + _ | + _
+
+julia> pcT
+A unitary Pauli channel P = ∑ ϕᵢ Pᵢ with the following branches:
+with ϕᵢ | Pᵢ
+ 0.853553+0.353553im | + _
+ 0.146447-0.353553im | + Z
+
+julia> apply!(StabMixture(S"-X"), pcT)
+A mixture ∑ ϕᵢⱼ Pᵢ ρ Pⱼ† where ρ is
+𝒟ℯ𝓈𝓉𝒶𝒷
++ Z
+𝒮𝓉𝒶𝒷
+- X
+with ϕᵢⱼ | Pᵢ | Pⱼ:
+ 0.0+0.353553im | + _ | + Z
+ 0.0-0.353553im | + Z | + _
+ 0.853553+0.0im | + _ | + _
+ 0.146447+0.0im | + Z | + Z
See also: PauliChannel
QuantumClifford.Stabilizer
— TypeStabilizer, i.e. a list of commuting multi-qubit Hermitian Pauli operators.
Instances can be created with the S
custom string macro or as direct sum of other stabilizers.
In many cases you probably would prefer to use the MixedDestabilizer
data structure, as it caries a lot of useful additional information, like tracking rank and destabilizer operators. Stabilizer
has mostly a pedagogical value, and it is also used for slightly faster simulation of a particular subset of Clifford operations.
julia> s = S"XXX
+ ZZI
+ IZZ"
++ XXX
++ ZZ_
++ _ZZ
+
+julia> s⊗s
++ XXX___
++ ZZ____
++ _ZZ___
++ ___XXX
++ ___ZZ_
++ ____ZZ
It has an indexing API, looking like a list of PauliOperator
s.
julia> s[2]
++ ZZ_
Pauli operators can act directly on the a stabilizer.
julia> P"YYY" * s
+- XXX
++ ZZ_
++ _ZZ
There are a number of ways to create a Stabilizer, including:
- generate Stabilizers from a list of Pauli operators
julia> Stabilizer([P"XX", P"ZZ"])
++ XX
++ ZZ
- generate Stabilizers from boolean matrices
julia> a = [true true; false false]; b = [false true; true true];
+
+julia> Stabilizer(a, b)
++ XY
++ ZZ
+
+julia> Stabilizer([0x0, 0x2], a, b)
++ XY
+- ZZ
- initialize an empty Stabilizer and fill it through indexing
julia> s = zero(Stabilizer, 2)
++ __
++ __
+
+julia> s[1,1] = (true, false); s
++ X_
++ __
There are no automatic checks for correctness (i.e. independence of all rows, commutativity of all rows, hermiticity of all rows). The rank (number of rows) is permitted to be less than the number of qubits (number of columns): canonilization, projection, etc. continue working in that case. To great extent this library uses the Stabilizer
data structure simply as a tableau. This might be properly abstracted away in future versions.
See also: PauliOperator
, canonicalize!
QuantumClifford.Stabilizer
— MethodConvert a graph representing a stabilizer state to an explicit Stabilizer.
See also: graphstate
QuantumClifford.UnbiasedUncorrelatedNoise
— TypeDepolarization noise model with total probability of error p
.
QuantumClifford.UnitaryPauliChannel
— TypeA Pauli channel datastructure, mainly for use with StabMixture
.
More convenient to use than PauliChannel
when you know your Pauli channel is unitary.
julia> Tgate = UnitaryPauliChannel(
+ (I, Z),
+ ((1+exp(im*π/4))/2, (1-exp(im*π/4))/2)
+ )
+A unitary Pauli channel P = ∑ ϕᵢ Pᵢ with the following branches:
+with ϕᵢ | Pᵢ
+ 0.853553+0.353553im | + _
+ 0.146447-0.353553im | + Z
+
+julia> PauliChannel(Tgate)
+Pauli channel ρ ↦ ∑ ϕᵢⱼ Pᵢ ρ Pⱼ† with the following branches:
+with ϕᵢⱼ | Pᵢ | Pⱼ:
+ 0.853553+0.0im | + _ | + _
+ 0.0+0.353553im | + _ | + Z
+ 0.0-0.353553im | + Z | + _
+ 0.146447+0.0im | + Z | + Z
QuantumClifford.VerifyOp
— TypeA "probe" to verify that the state of the qubits corresponds to a desired good_state
, e.g. at the end of the execution of a circuit.
QuantumClifford.sCNOT
— TypeA "symbolic" CNOT. See also: AbstractSymbolicOperator
QuantumClifford.sCPHASE
— TypeA "symbolic" CPHASE. See also: AbstractSymbolicOperator
QuantumClifford.sCXYZ
— TypeA "symbolic" single-qubit CXYZ. See also: SingleQubitOperator
, AbstractSymbolicOperator
QuantumClifford.sCZYX
— TypeA "symbolic" single-qubit CZYX. See also: SingleQubitOperator
, AbstractSymbolicOperator
QuantumClifford.sHadamard
— TypeA "symbolic" single-qubit Hadamard. See also: SingleQubitOperator
, AbstractSymbolicOperator
QuantumClifford.sHadamardXY
— TypeA "symbolic" single-qubit HadamardXY. See also: SingleQubitOperator
, AbstractSymbolicOperator
QuantumClifford.sHadamardYZ
— TypeA "symbolic" single-qubit HadamardYZ. See also: SingleQubitOperator
, AbstractSymbolicOperator
QuantumClifford.sId1
— TypeA "symbolic" single-qubit Identity operation.
See also: SingleQubitOperator
QuantumClifford.sInvPhase
— TypeA "symbolic" single-qubit InvPhase. See also: SingleQubitOperator
, AbstractSymbolicOperator
QuantumClifford.sInvSQRTX
— TypeA "symbolic" single-qubit InvSQRTX. See also: SingleQubitOperator
, AbstractSymbolicOperator
QuantumClifford.sInvSQRTY
— TypeA "symbolic" single-qubit InvSQRTY. See also: SingleQubitOperator
, AbstractSymbolicOperator
QuantumClifford.sInvZCrY
— TypeA "symbolic" InvZCrY. See also: AbstractSymbolicOperator
QuantumClifford.sMRX
— TypeQuantumClifford.sMRY
— TypeQuantumClifford.sMRZ
— TypeMeasure a qubit in the Z basis and reset to the |0⟩ state.
As described below there is a difference between measuring the qubit (followed by setting it to a given known state) and "tracing out" the qubit. By reset here we mean "measuring and setting to a known state", not "tracing out".
julia> s = MixedDestabilizer(S"XXX ZZI IZZ") # |000⟩+|111⟩
+𝒟ℯ𝓈𝓉𝒶𝒷
++ Z__
++ _X_
++ __X
+𝒮𝓉𝒶𝒷━
++ XXX
++ ZZ_
++ Z_Z
+
+julia> traceout!(copy(s), 1) # = I⊗(|00⟩⟨00| + |11⟩⟨11|)
+𝒟ℯ𝓈𝓉𝒶𝒷
++ _X_
+𝒳ₗ━━━
++ _XX
++ Z__
+𝒮𝓉𝒶𝒷━
++ _ZZ
+𝒵ₗ━━━
++ Z_Z
++ XXX
+
+julia> projectZ!(traceout!(copy(s), 1), 1)[1] # = |000⟩⟨000|+|011⟩⟨011| or |100⟩⟨100|+|111⟩⟨111| (use projectZrand! to actually get a random result)
+𝒟ℯ𝓈𝓉𝒶𝒷
++ _X_
++ XXX
+𝒳ₗ━━━
++ _XX
+𝒮𝓉𝒶𝒷━
++ _ZZ
++ Z__
+𝒵ₗ━━━
++ Z_Z
+
+julia> projectZ!(copy(s), 1)[1] # = |000⟩ or |111⟩ (use projectZrand! to actually get a random result)
+𝒟ℯ𝓈𝓉𝒶𝒷
++ XXX
++ _X_
++ __X
+𝒮𝓉𝒶𝒷━
++ Z__
++ ZZ_
++ Z_Z
julia> apply!(Register(copy(s)), sMRZ(1)) |> quantumstate # |000⟩ or |011⟩, depending on randomization
+𝒟ℯ𝓈𝓉𝒶𝒷
++ XXX
++ _X_
++ __X
+𝒮𝓉𝒶𝒷━
++ Z__
+- ZZ_
+- Z_Z
QuantumClifford.sMX
— TypeSymbolic single qubit X measurement. See also Register
, projectXrand!
, sMY
, sMZ
QuantumClifford.sMY
— TypeSymbolic single qubit Y measurement. See also Register
, projectYrand!
, sMX
, sMZ
QuantumClifford.sMZ
— TypeSymbolic single qubit Z measurement. See also Register
, projectZrand!
, sMX
, sMY
QuantumClifford.sPhase
— TypeA "symbolic" single-qubit Phase. See also: SingleQubitOperator
, AbstractSymbolicOperator
QuantumClifford.sSQRTX
— TypeA "symbolic" single-qubit SQRTX. See also: SingleQubitOperator
, AbstractSymbolicOperator
QuantumClifford.sSQRTY
— TypeA "symbolic" single-qubit SQRTY. See also: SingleQubitOperator
, AbstractSymbolicOperator
QuantumClifford.sSWAP
— TypeA "symbolic" SWAP. See also: AbstractSymbolicOperator
QuantumClifford.sX
— TypeA "symbolic" single-qubit X. See also: SingleQubitOperator
, AbstractSymbolicOperator
QuantumClifford.sXCX
— TypeA "symbolic" XCX. See also: AbstractSymbolicOperator
QuantumClifford.sXCY
— TypeA "symbolic" XCY. See also: AbstractSymbolicOperator
QuantumClifford.sXCZ
— TypeA "symbolic" XCZ. See also: AbstractSymbolicOperator
QuantumClifford.sY
— TypeA "symbolic" single-qubit Y. See also: SingleQubitOperator
, AbstractSymbolicOperator
QuantumClifford.sYCX
— TypeA "symbolic" YCX. See also: AbstractSymbolicOperator
QuantumClifford.sYCY
— TypeA "symbolic" YCY. See also: AbstractSymbolicOperator
QuantumClifford.sYCZ
— TypeA "symbolic" YCZ. See also: AbstractSymbolicOperator
QuantumClifford.sZ
— TypeA "symbolic" single-qubit Z. See also: SingleQubitOperator
, AbstractSymbolicOperator
QuantumClifford.sZCX
— TypeA "symbolic" ZCX. See also: AbstractSymbolicOperator
QuantumClifford.sZCY
— TypeA "symbolic" ZCY. See also: AbstractSymbolicOperator
QuantumClifford.sZCZ
— TypeA "symbolic" ZCZ. See also: AbstractSymbolicOperator
QuantumClifford.sZCrY
— TypeA "symbolic" ZCrY. See also: AbstractSymbolicOperator
QuantumClifford.PauliError
— FunctionA convenient constructor for various types of Pauli errors, that can be used as circuit gates in simulations. Returns more specific types when necessary.
QuantumClifford.PauliError
— Method"Construct a gate operation that applies a biased Pauli error on all qubits
independently, each with probabilities px
, py
, pz
. Note that the probability of any error occurring is px+py+pz
. Because of this, PauliError(1, p)
is equivalent to PauliError(1,p/3,p/3,p/3)
. Similarly, if one wanted to exclude Z errors from PauliError(1,p/3,p/3,p/3)
while mainting the same rate of X errors, one could write PauliError(1, p*2/3, 0, 0)
(in the sense that Y errors can be interpreted as an X and a Z happening at the same time).
QuantumClifford.PauliError
— Method"Construct a gate operation that applies an unbiased Pauli error on all qubits
, each with independent probability p
.
QuantumClifford.PauliError
— Method"Construct a gate operation that applies a biased Pauli error on qubit q
with independent probabilities px
, py
, pz
. Note that the probability of any error occurring is px+py+pz
. Because of this, PauliError(1, p)
is equivalent to PauliError(1,p/3,p/3,p/3)
. Similarly, if one wanted to exclude Z errors from PauliError(1,p/3,p/3,p/3)
while mainting the same rate of X errors, one could write PauliError(1, p*2/3, 0, 0)
(in the sense that Y errors can be interpreted as an X and a Z happening at the same time).
QuantumClifford.PauliError
— Method"Construct a gate operation that applies an unbiased Pauli error on qubit q
with probability p
.
QuantumClifford.affectedqubits
— FunctionA method giving the qubits acted upon by a given operation. Part of the Noise interface.
QuantumClifford.applybranches
— FunctionCompute all possible new states after the application of the given operator. Reports the probability of each one of them. Deterministic (as it reports all branches of potentially random processes), part of the Perturbative Expansion interface.
QuantumClifford.applynoise!
— FunctionA method modifying a given state by applying the corresponding noise model. It is non-deterministic, part of the Noise interface.
QuantumClifford.applywstatus!
— MethodUsed for mctrajectories
.
QuantumClifford.bell
— FunctionPrepare one or more Bell pairs (with optional phases).
julia> bell()
++ XX
++ ZZ
+
+julia> bell(2)
++ XX__
++ ZZ__
++ __XX
++ __ZZ
+
+julia> bell((true, false))
+- XX
++ ZZ
+
+julia> bell([true, false, true, true])
+- XX__
++ ZZ__
+- __XX
+- __ZZ
QuantumClifford.bigram
— Methodbigram(
+ state::QuantumClifford.AbstractStabilizer;
+ clip
+) -> Matrix{Int64}
+
Get the bigram of a tableau.
It is the list of endpoints of a tableau in the clipped gauge.
If clip=true
(the default) the tableau is converted to the clipped gauge in-place before calculating the bigram. Otherwise, the clip gauge conversion is skipped (for cases where the input is already known to be in the correct gauge).
Introduced in (Nahum et al., 2017), with a more detailed explanation of the algorithm in (Li et al., 2019) and (Gullans et al., 2021).
See also: canonicalize_clip!
QuantumClifford.bitview
— FunctionA view of the classical bits stored with the state
QuantumClifford.canonicalize!
— Methodcanonicalize!(
+ state::QuantumClifford.AbstractStabilizer;
+ phases,
+ ranks
+) -> Union{Tuple{QuantumClifford.AbstractStabilizer, Int64, Int64}, QuantumClifford.AbstractStabilizer}
+
Canonicalize a stabilizer (in place).
Assumes the input is a valid stabilizer (all operators commute and have real phases). It permits redundant generators and identity generators.
julia> ghz = S"XXXX
+ ZZII
+ IZZI
+ IIZZ";
+
+
+julia> canonicalize!(ghz)
++ XXXX
++ Z__Z
++ _Z_Z
++ __ZZ
+
+julia> canonicalize!(S"XXXX
+ IZZI
+ IIZZ")
++ XXXX
++ _Z_Z
++ __ZZ
Not all rows in the tableau in the next example are independent:
julia> canonicalize!(S"XXXX
+ ZZII
+ IZZI
+ IZIZ
+ IIZZ")
++ XXXX
++ Z__Z
++ _Z_Z
++ __ZZ
++ ____
In cases of lower rank, more advanced tableau structures might be better. For instance the MixedStabilizer
or MixedDestabilizer
structures (you can read more about them in the Data Structures section of the documentation).
If phases=false
is set, the canonicalization does not track the phases in the tableau, leading to significant (constant factor) speedup.
julia> s = S"-ZX
+ XZ"
+- ZX
++ XZ
+
+julia> canonicalize!(copy(s), phases=false)
+- XZ
++ ZX
+
+julia> canonicalize!(copy(s))
++ XZ
+- ZX
If ranks=true
is set, the last pivot indices for the X and Z stage of the canonicalization are returned as well.
julia> s = S"XXXX
+ ZZII
+ IZIZ
+ ZIIZ";
+
+
+julia> _, ix, iz = canonicalize!(s, ranks=true); ix, iz
+(1, 3)
+
+julia> s
++ XXXX
++ Z__Z
++ _Z_Z
++ ____
Based on (Garcia et al., 2012).
See also: canonicalize_rref!
, canonicalize_gott!
QuantumClifford.canonicalize_clip!
— Methodcanonicalize_clip!(
+ state::QuantumClifford.AbstractStabilizer;
+ phases
+) -> QuantumClifford.AbstractStabilizer
+
Fix the clipped gauge of a stabilizer (in place).
Assumes the input is a valid full-rank stabilizer (all operators commute and have real phases).
julia> s = S"- X_ZX_X
+ + XXYZ__
+ - YZ_Z_X
+ - XZX__Y
+ + _Z_Y_Y
+ - ____Z_";
+
+
+julia> canonicalize_clip!(s)
+- X_XY__
++ YZY___
++ _XZX__
+- _ZYX_Z
+- __YZ_X
+- ____Z_
If phases=false
is set, the canonicalization does not track the phases in the tableau, leading to a significant speedup.
Introduced in (Nahum et al., 2017), with a more detailed explanation of the algorithm in Appendix A of (Li et al., 2019)
See also: canonicalize!
, canonicalize_rref!
, canonicalize_gott!
.
QuantumClifford.canonicalize_gott!
— MethodInplace Gottesman canonicalization of a tableau.
This uses different canonical form from canonicalize!
. It is used in the computation of the logical X and Z operators of a MixedDestabilizer
.
It returns the (in place) modified state, the indices of the last pivot of both Gaussian elimination steps, and the permutations that have been used to put the X and Z tableaux in standard form.
Based on (Gottesman, 1997).
See also: canonicalize!
, canonicalize_rref!
QuantumClifford.canonicalize_rref!
— Methodcanonicalize_rref!(
+ state::QuantumClifford.AbstractStabilizer,
+ colindices;
+ phases
+) -> Tuple{QuantumClifford.AbstractStabilizer, Any}
+
Canonicalize a stabilizer (in place) along only some columns.
This uses different canonical form from canonicalize!
. It also indexes in reverse in order to make its use in traceout!
more efficient. Its use in traceout!
is its main application.
It returns the (in place) modified state and the index of the last pivot.
Based on (Audenaert and Plenio, 2005).
See also: canonicalize!
, canonicalize_gott!
QuantumClifford.canonicalize_rref!
— Methodcanonicalize_rref!(
+ state::QuantumClifford.AbstractStabilizer;
+ phases
+) -> Tuple{QuantumClifford.AbstractStabilizer, Any}
+
QuantumClifford.centralizer
— MethodFor a given set of Paulis (in the form of a Tableau
), return the subset of Paulis that commute with all Paulis in set.
julia> centralizer(T"XX ZZ _Z")
++ ZZ
QuantumClifford.clifford_cardinality
— MethodThe size of the Clifford group 𝒞
over a given number of qubits, possibly modulo the phases.
For n qubits, not accounting for phases is 2ⁿⁿΠⱼ₌₁ⁿ(4ʲ-1)
. There are 4ⁿ
different phase configurations.
julia> clifford_cardinality(7)
+457620995529680351512370381586432000
When not accounting for phases (phases = false
) the result is the same as the size of the Symplectic group Sp(2n) ≡ 𝒞ₙ/𝒫ₙ
, where 𝒫ₙ
is the Pauli group over n
qubits.
julia> clifford_cardinality(7, phases=false)
+27930968965434591767112450048000
See also: enumerate_cliffords
.
QuantumClifford.comm
— FunctionCheck whether two operators commute.
0x0
if they commute, 0x1
if they anticommute.
julia> P"XX"*P"ZZ", P"ZZ"*P"XX"
+(- YY, - YY)
+
+julia> comm(P"ZZ", P"XX")
+0x00
+
+julia> comm(P"IZ", P"XX")
+0x01
See also: comm!
QuantumClifford.comm!
— FunctionAn in-place version of comm
, storing its output in the given buffer.
QuantumClifford.compactify_circuit
— MethodConvert a list of gates to a more optimized "sum type" format which permits faster dispatch.
Generally, this should be called on a circuit before it is used in a simulation.
QuantumClifford.contractor
— MethodReturn the subset of Paulis in a Stabilizer that have identity operators on all qubits corresponding to the given subset, without the entries corresponding to subset.
julia> contractor(S"_X X_", [1])
++ X
QuantumClifford.delete_columns
— MethodReturn the given stabilizer without all the qubits in the given iterable.
The resulting tableaux is not guaranteed to be valid (to retain its commutation relationships).
julia> delete_columns(S"XYZ YZX ZXY", [1,3])
++ Y
++ Z
++ X
See also: traceout!
QuantumClifford.destabilizerview
— MethodA view of the subtableau corresponding to the destabilizer. See also tab
, stabilizerview
, logicalxview
, logicalzview
QuantumClifford.enumerate_cliffords
— MethodGive the i-th n-qubit Clifford operation, where i∈{1..2ⁿⁿΠⱼ₌₁ⁿ(4ʲ-1)}
The algorithm is detailed in (Koenig and Smolin, 2014).
See also: symplecticGS
, clifford_cardinality
.
QuantumClifford.enumerate_cliffords
— MethodGive all n-qubit Clifford operations.
The algorithm is detailed in (Koenig and Smolin, 2014).
See also: symplecticGS
, clifford_cardinality
.
QuantumClifford.enumerate_phases
— MethodGiven an operator, return all operators that have the same tableau but different phases.
julia> length(collect(enumerate_phases(tCNOT)))
+16
See also: enumerate_cliffords
, clifford_cardinality
.
QuantumClifford.enumerate_phases
— MethodGiven a set of operators, return all operators that have the same tableaux but different phases.
julia> length(collect(enumerate_phases(enumerate_cliffords(2))))
+11520
See also: enumerate_cliffords
, clifford_cardinality
.
QuantumClifford.enumerate_single_qubit_gates
— MethodGenerate a symbolic single-qubit gate given its index. Optionally, set non-trivial phases.
julia> enumerate_single_qubit_gates(6)
+sPhase on qubit 1
+X₁ ⟼ + Y
+Z₁ ⟼ + Z
+
+julia> enumerate_single_qubit_gates(6, qubit=2, phases=(true, true))
+SingleQubitOperator on qubit 2
+X₁ ⟼ - Y
+Z₁ ⟼ - Z
See also: enumerate_cliffords
.
QuantumClifford.fastcolumn
— FunctionConvert a tableau to a memory layout that is fast for column operations.
In this layout a column of the tableau is stored (mostly) contiguously in memory. Due to bitpacking, e.g., packing 64 bits into a single UInt64
, the memory layout is not perfectly contiguous, but it is still optimal given that some bitwrangling is required to extract a given bit.
See also: fastrow
QuantumClifford.fastrow
— FunctionConvert a tableau to a memory layout that is fast for row operations.
In this layout a Pauli string (a row of the tableau) is stored contiguously in memory.
See also: fastrow
QuantumClifford.generate!
— MethodGenerate a Pauli operator by using operators from a given the Stabilizer.
It assumes the stabilizer is already canonicalized. It modifies the Pauli operator in place, generating it in reverse, up to a phase. That phase is left in the modified operator, which should be the identity up to a phase. Returns the new operator and the list of indices denoting the elements of stabilizer
that were used for the generation.
julia> ghz = S"XXXX
+ ZZII
+ IZZI
+ IIZZ";
+
+
+julia> canonicalize!(ghz)
++ XXXX
++ Z__Z
++ _Z_Z
++ __ZZ
+
+julia> generate!(P"-ZIZI", ghz)
+(- ____, [2, 4])
When the Pauli operator can not be generated by the given tableau, nothing
is returned.
julia> generate!(P"XII",canonicalize!(S"ZII")) === nothing
+true
+
+julia> generate!(P"XII",canonicalize!(S"XII")) === nothing
+false
QuantumClifford.gf2_H_to_G
— MethodFor a given F(2,2) parity check matrix, return the generator matrix.
QuantumClifford.gf2_gausselim!
— MethodGaussian elimination over the binary field.
QuantumClifford.gf2_invert
— MethodInvert a binary matrix.
QuantumClifford.gf2_isinvertible
— MethodCheck whether a binary matrix is invertible.
QuantumClifford.ghz
— FunctionPrepare a GHZ state of n qubits.
julia> ghz()
++ XXX
++ ZZ_
++ _ZZ
+
+julia> ghz(2)
++ XX
++ ZZ
+
+julia> ghz(4)
++ XXXX
++ ZZ__
++ _ZZ_
++ __ZZ
QuantumClifford.graph_gate
— MethodA helper function converting the gate indices from graphstate
into a Clifford operator.
julia> s = S" XXX
+ YZ_
+ -_ZZ";
+
+
+julia> graph, h_idx, ip_idx, z_idx = graphstate(s);
+
+
+julia> gate = graph_gate(h_idx, ip_idx, z_idx, nqubits(s));
+
+
+julia> apply!(s, gate) # This is now a graph state (notice you need to multiply row 1 by row 2)
++ YYZ
++ XZ_
++ _ZX
+
+julia> canonicalize!(s) == canonicalize!(Stabilizer(graph))
+true
See also: graph_gatesequence
QuantumClifford.graph_gatesequence
— MethodA helper function converting the gate indices from graphstate
into a sequence of gates.
julia> s = S" XXX
+ YZ_
+ -_ZZ";
+
+
+julia> graph, h_idx, ip_idx, z_idx = graphstate(s);
+
+
+julia> gates = graph_gatesequence(h_idx, ip_idx, z_idx);
+
+
+julia> for gate in vcat(gates...) apply!(s, gate) end
+
+
+julia> s # This is now a graph state (notice you need to multiply row 1 by row 2)
++ YYZ
++ XZ_
++ _ZX
+
+julia> canonicalize!(s) == canonicalize!(Stabilizer(graph))
+true
See also: graph_gatesequence
QuantumClifford.graphstate!
— MethodAn in-place version of graphstate
.
QuantumClifford.graphstate
— MethodConvert any stabilizer state to a graph state
Graph states are a special type of entangled stabilizer states that can be represented by a graph. For a graph $G=(V,E)$ the corresponding stabilizers are $S_v = X_v \prod_{u ∈ N(v)} Z_u$. Notice that such tableau rows contain only a single X operator. There is a set of single qubit gates that converts any stabilizer state to a graph state.
This function returns the graph state corresponding to a stabilizer and the gates that might be necessary to convert the stabilizer into a state representable as a graph.
For a tableau stab
you can convert it with:
graph, hadamard_idx, iphase_idx, flips_idx = graphstate()
where graph
is the graph representation of stab
, and the rest specifies the single-qubit gates converting stab
to graph
: hadamard_idx
are the qubits that require a Hadamard gate (mapping X ↔ Z), iphase_idx
are (different) qubits that require an inverse Phase gate (Y → X), and flips_idx
are the qubits that require a phase flip (Pauli Z gate), after the previous two sets of gates.
julia> using Graphs
+
+julia> s = S" XXX
+ ZZ_
+ -_ZZ";
+
+
+julia> g, h_idx, ip_idx, z_idx = graphstate(s);
+
+
+julia> collect(edges(g))
+2-element Vector{Graphs.SimpleGraphs.SimpleEdge{Int64}}:
+ Edge 1 => 2
+ Edge 1 => 3
+
+julia> h_idx
+2-element Vector{Int64}:
+ 2
+ 3
+
+julia> ip_idx
+Int64[]
+
+julia> z_idx
+1-element Vector{Int64}:
+ 3
The Graphs.jl
library provides many graph-theory tools and the MakieGraphs.jl
library provides plotting utilities for graphs.
You can directly call the graph constructor on a stabilizer, if you just want the graph and do not care about the Clifford operation necessary to convert an arbitrary state to a state representable as a graph:
julia> collect(edges( Graph(bell()) ))
+1-element Vector{Graphs.SimpleGraphs.SimpleEdge{Int64}}:
+ Edge 1 => 2
For a version that does not copy the stabilizer, but rather performs transformations in-place, use graphstate!
. It would perform canonicalize_gott!
on its argument as it finds a way to convert it to a graph state.
QuantumClifford.groupify
— MethodReturn the full stabilizer group represented by the input generating set (a Stabilizer
).
The returned object is exponentially long.
julia> groupify(S"XZ ZX")
++ __
++ XZ
++ ZX
++ YY
QuantumClifford.logdot
— MethodLogarithm of the inner product between to Stabilizer states.
If the result is nothing
, the dot inner product is zero. Otherwise the inner product is 2^(-logdot/2)
.
The actual inner product can be computed with LinearAlgebra.dot
.
Based on (Garcia et al., 2012).
QuantumClifford.logicalxview
— MethodA view of the subtableau corresponding to the logical X operators. See also tab
, stabilizerview
, destabilizerview
, logicalzview
QuantumClifford.logicalzview
— MethodA view of the subtableau corresponding to the logical Z operators. See also tab
, stabilizerview
, destabilizerview
, logicalxview
QuantumClifford.mctrajectories
— MethodRun multiple Monte Carlo trajectories and report the aggregate final statuses of each.
See also: pftrajectories
, petrajectories
QuantumClifford.mctrajectory!
— MethodRun a single Monte Carlo sample, starting with (and modifying) state
by applying the given circuit
. Uses apply!
under the hood.
QuantumClifford.minimal_generating_set
— MethodFor a not-necessarily-minimal generating set, return the minimal generating set.
The input has to have only real phases.
julia> minimal_generating_set(S"__ XZ ZX YY")
++ XZ
++ ZX
QuantumClifford.normalizer
— MethodReturn all Pauli operators with the same number of qubits as the given Tableau
t
that commute with all operators in t
.
julia> normalizer(T"X")
++ _
++ X
QuantumClifford.pauligroup
— MethodReturn the full Pauli group of a given length. Phases are ignored by default, but can be included by setting phases=true
.
julia> pauligroup(1)
++ _
++ X
++ Z
++ Y
+
+julia> pauligroup(1, phases=true)
++ _
++ X
++ Z
++ Y
+- _
+- X
+- Z
+- Y
++i_
++iX
++iZ
++iY
+-i_
+-iX
+-iZ
+-iY
QuantumClifford.petrajectories
— MethodRun a perturbative expansion to a given order. This is the main public function for the perturbative expansion approach.
See also: pftrajectories
, mctrajectories
QuantumClifford.pfmeasurements
— Methodpfmeasurements(frame::PauliFrame) -> Any
+
Returns the measurement results for each frame in the PauliFrame
instance.
The return measurements are relative to the reference measurements, i.e. they only say whether the reference measurements have been flipped in the given frame.
QuantumClifford.pfmeasurements
— Methodpfmeasurements(register::Register, frame::PauliFrame) -> Any
+
Takes the references measurements from the given Register
and applies the flips as prescribed by the PauliFrame
relative measurements. The result is the actual (non-relative) measurement results for each frame.
QuantumClifford.pfmeasurements
— Methodpfmeasurements(register::Register) -> Vector{Bool}
+
Returns the measurements stored in the bits of the given Register
.
QuantumClifford.pftrajectories
— FunctionPerform a "Pauli frame" style simulation of a quantum circuit.
QuantumClifford.pftrajectories
— Methodpftrajectories(
+ circuit;
+ trajectories,
+ threads
+) -> PauliFrame{Stabilizer{QuantumClifford.Tableau{Vector{UInt8}, LinearAlgebra.Adjoint{UInt64, Matrix{UInt64}}}}, Matrix{Bool}}
+
The main method for running Pauli frame simulations of circuits. See the other methods for lower level access.
Multithreading is enabled by default, but can be disabled by setting threads=false
. Do not forget to launch Julia with multiple threads enabled, e.g. julia -t4
, if you want to use multithreading.
See also: mctrajectories
, petrajectories
QuantumClifford.pftrajectories
— Methodpftrajectories(state::PauliFrame, circuit) -> PauliFrame
+
Evolve each frame stored in PauliFrame
by the given circuit.
QuantumClifford.pftrajectories
— Methodpftrajectories(
+ register::Register,
+ circuit;
+ trajectories
+) -> Tuple{Register, PauliFrame{Stabilizer{QuantumClifford.Tableau{Vector{UInt8}, LinearAlgebra.Adjoint{UInt64, Matrix{UInt64}}}}, Matrix{Bool}}}
+
For a given Register
and circuit, simulates the reference circuit acting on the register and then also simulate numerous PauliFrame
trajectories. Returns the register and the PauliFrame
instance.
Use pfmeasurements
to get the measurement results.
QuantumClifford.phases
— MethodThe phases of a given tableau. It is a view, i.e. if you modify this array, the original tableau caries these changes.
QuantumClifford.prodphase
— MethodGet the phase of the product of two Pauli operators.
Phase is encoded as F(4) in the low qubits of an UInt8.
julia> P"ZZZ"*P"XXX"
+-iYYY
+
+julia> prodphase(P"ZZZ", P"XXX")
+0x03
+
+julia> prodphase(P"XXX", P"ZZZ")
+0x01
QuantumClifford.projectXrand!
— MethodprojectXrand!(state, qubit) -> Tuple{Register, UInt8}
+
Project qubit
of state
along the X axis and randomize the phase if necessary.
Lower boilerplate version of project!
.
See also: project!
, projectX!
, projectZrand!
, projectYrand!
QuantumClifford.projectYrand!
— MethodprojectYrand!(state, qubit) -> Tuple{Register, UInt8}
+
Project qubit
of state
along the Y axis and randomize the phase if necessary.
Lower boilerplate version of project!
.
See also: project!
, projectY!
, projectXrand!
, projectZrand!
QuantumClifford.projectZrand!
— MethodprojectZrand!(state, qubit) -> Tuple{Register, UInt8}
+
Project qubit
of state
along the Z axis and randomize the phase if necessary.
Lower boilerplate version of project!
.
See also: project!
, projectZ!
, projectXrand!
, projectYrand!
QuantumClifford.projectrand!
— Methodprojectrand!(state, pauli) -> Tuple{Register, Any}
+
Measure pauli
operator on state
and randomize the phase if necessary.
Lower boilerplate version of project!
.
See also: project!
, projectXrand!
, projectZrand!
, projectYrand!
QuantumClifford.puttableau!
— MethodPut source tableau in target tableau at given row and column. Assumes target location is zeroed out.
QuantumClifford.quantumstate
— FunctionOnly the quantum part of the state (excluding classical bits)
QuantumClifford.random_all_to_all_clifford_circuit
— MethodRandom all-to-all Clifford circuit.
The circuit contains nqubits
qubits and ngates
gates. The connectivity is all to all. Each gate in the circuit is a random 2-qubit Clifford gate on randomly picked two qubits.
QuantumClifford.random_brickwork_clifford_circuit
— MethodRandom brickwork Clifford circuit.
The connectivity of the random circuit is brickwork in some dimensions. Each gate in the circuit is a random 2-qubit Clifford gate.
The brickwork is defined as follows: The qubits are arranged as a lattice, and lattice_size
contains side length in each dimension. For example, a chain of length five will have lattice_size = (5,)
, and a 5×5 lattice will have lattice_size = (5, 5)
.
In multi-dimensional cases, gate layers act alternatively along each direction. The nearest two layers along the same direction are offset by one qubit, forming a so-called brickwork. The boundary condition is chosen as open.
QuantumClifford.random_clifford
— MethodA random Clifford operator generated by the Bravyi-Maslov Algorithm 2 from (Bravyi and Maslov, 2021).
QuantumClifford.random_clifford1
— MethodRandom symbolic single-qubit Clifford applied to qubit at index qubit
.
See also: SingleQubitOperator
, random_clifford
QuantumClifford.random_destabilizer
— MethodA random Stabilizer/Destabilizer tableau generated by the Bravyi-Maslov Algorithm 2 from (Bravyi and Maslov, 2021).
random_destabilizer(n)
gives a n-qubit tableau of rank n
. random_destabilizer(r,n)
gives a n-qubit tableau of rank r
.
QuantumClifford.random_pauli
— FunctionA random Pauli operator on n qubits.
Use nophase=false
to randomize the phase. Use realphase=false
to get operators with phases including ±i.
Optionally, a "flip" probability p
can be provided specified, in which case each bit is set to I with probability 1-p
and to X or Y or Z with probability p
. Useful for simulating unbiased Pauli noise.
See also random_pauli!
QuantumClifford.random_pauli!
— FunctionAn in-place version of random_pauli
QuantumClifford.random_stabilizer
— MethodA random Stabilizer tableau generated by the Bravyi-Maslov Algorithm 2 from (Bravyi and Maslov, 2021).
QuantumClifford.single_x
— MethodQuantumClifford.single_y
— MethodQuantumClifford.single_z
— MethodQuantumClifford.stab_to_gf2
— MethodThe F(2,2) matrix of a given tableau, represented as the concatenation of two binary matrices, one for X and one for Z.
QuantumClifford.stabilizerplot
— FunctionA Makie.jl recipe for pictorial representation of a tableau.
Requires a Makie.jl backend to be loaded, e.g. using CairoMakie
.
Alternatively, you can use the Plots.jl plotting ecosystem, e.g. using Plots; plot(S"XXX ZZZ")
.
Consult the documentation for more details on visualization options.
QuantumClifford.stabilizerplot_axis
— FunctionA Makie.jl recipe for pictorial representation of a tableau.
Requires a Makie.jl backend to be loaded, e.g. using CairoMakie
.
Alternatively, you can use the Plots.jl plotting ecosystem, e.g. using Plots; plot(S"XXX ZZZ")
.
Consult the documentation for more details on visualization options.
QuantumClifford.stabilizerview
— MethodA view of the subtableau corresponding to the stabilizer. See also tab
, destabilizerview
, logicalxview
, logicalzview
QuantumClifford.symplecticGS
— MethodPerform the Symplectic Gram-Schmidt procedure that gives a Clifford operator canonically related to a given Pauli operator.
The algorithm is detailed in (Koenig and Smolin, 2014).
julia> symplecticGS(P"X", padded_n=3)
+X₁ ⟼ + X__
+X₂ ⟼ + _X_
+X₃ ⟼ + __X
+Z₁ ⟼ + Z__
+Z₂ ⟼ + _Z_
+Z₃ ⟼ + __Z
+
+julia> symplecticGS(P"Z")
+X₁ ⟼ + Z
+Z₁ ⟼ + X
See also: enumerate_cliffords
, clifford_cardinality
.
QuantumClifford.tab
— MethodExtract the underlying tableau structure.
julia> s = S"X"
++ X
+
+julia> tab(s)
++ X
+
+julia> tab(Destabilizer(s))
++ Z
++ X
+
+julia> tab(MixedDestabilizer(s))
++ Z
++ X
+
+julia> tab(tHadamard)
++ Z
++ X
+
+julia> typeof(tab(tHadamard))
+QuantumClifford.Tableau{Vector{UInt8}, Matrix{UInt64}}
See also: stabilizerview
, destabilizerview
, logicalxview
, logicalzview
QuantumClifford.xbit
— MethodExtract as a new bit array the X part of the UInt
array of packed qubits of a given Pauli operator.
QuantumClifford.xview
— MethodGet a view of the X part of the UInt
array of packed qubits of a given Pauli operator.
QuantumClifford.zbit
— MethodExtract as a new bit array the Z part of the UInt
array of packed qubits of a given Pauli operator.
QuantumClifford.zview
— MethodGet a view of the Y part of the UInt
array of packed qubits of a given Pauli operator.
QuantumInterface.apply!
— FunctionIn QuantumClifford
the apply!
function is used to apply any quantum operation to a stabilizer state, including unitary Clifford operations, Pauli measurements, and noise. Thus, this function may result in a random/stochastic result (e.g. with measurements or noise).
QuantumInterface.embed
— MethodEmbed a Pauli operator in a larger Pauli operator.
julia> embed(5, 3, P"-Y")
+- __Y__
+
+julia> embed(5, (3,5), P"-YX")
+- __Y_X
QuantumInterface.entanglement_entropy
— FunctionGet bipartite entanglement entropy of a subsystem
Defined as entropy of the reduced density matrix.
It can be calculated with multiple different algorithms, the most performant one depending on the particular case.
Currently implemented are the :clip
(clipped gauge), :graph
(graph state), and :rref
(Gaussian elimination) algorithms. Benchmark your particular case to choose the best one.
QuantumInterface.entanglement_entropy
— MethodGet bipartite entanglement entropy by first converting the state to a graph and computing the rank of the adjacency matrix.
Based on "Entanglement in graph states and its applications".
QuantumInterface.entanglement_entropy
— MethodGet bipartite entanglement entropy by converting to RREF form (i.e., partial trace form).
The state will be partially canonicalized in an RREF form.
See also: canonicalize_rref!
, traceout!
.
QuantumInterface.entanglement_entropy
— MethodGet bipartite entanglement entropy of a contiguous subsystem by passing through the clipped gauge.
If clip=false
is set the canonicalization step is skipped, useful if the input state is already in the clipped gauge.
See also: bigram
, canonicalize_clip!
QuantumInterface.expect
— Methodexpect(p::PauliOperator, st::AbstractStabilizer)
Compute the expectation value of a Pauli operator p
on a stabilizer state st
. This function will allocate a temporary copy of the stabilizer state st
.
QuantumInterface.nqubits
— MethodThe number of qubits of a given state.
QuantumInterface.project!
— Methodproject!(
+ state,
+ pauli::PauliOperator;
+ keep_result,
+ phases
+) -> Tuple{MixedStabilizer, Any, Any}
+
Project the state of a Stabilizer on the two eigenspaces of a Pauli operator.
Assumes the input is a valid stabilizer. The projection is done inplace on that stabilizer and it does not modify the projection operator.
It returns
- a stabilizer that might not be in canonical form
- the index of the row where the non-commuting operator was (that row is now equal to
pauli
; its phase is not updated and for a faithful measurement simulation it needs to be randomized by the user) - and the result of the projection if there was no non-commuting operator (
nothing
otherwise)
If keep_result==false
that result of the projection in case of anticommutation is not computed, sparing a canonicalization operation. This canonicalization operation is the only one potentially of cubic complexity. The rest of the calculations are of quadratic complexity.
If you need to measure a single qubit instead of a multiqubit Pauli operator, the faster projectX!
, projectY!
, and projectZ!
are available.
For less boilerplate and automatic randomization of the phase use projectrand!
.
Here is an example of a projection destroying entanglement:
julia> ghz = S"XXXX
+ ZZII
+ IZZI
+ IIZZ";
+
+
+julia> canonicalize!(ghz)
++ XXXX
++ Z__Z
++ _Z_Z
++ __ZZ
+
+julia> state, anticom_index, result = project!(ghz, P"ZIII");
+
+
+julia> state
++ Z___
++ Z__Z
++ _Z_Z
++ __ZZ
+
+julia> canonicalize!(state)
++ Z___
++ _Z__
++ __Z_
++ ___Z
+
+julia> anticom_index, result
+(1, nothing)
And an example of projection consistent with the stabilizer state.
julia> s = S"ZII
+ IXI
+ IIY";
+
+
+julia> canonicalize!(s)
++ _X_
++ __Y
++ Z__
+
+julia> state, anticom_index, result = project!(s, P"-ZII");
+
+
+julia> state
++ _X_
++ __Y
++ Z__
+
+julia> anticom_index, result
+(0, 0x02)
While not the best choice, Stabilizer
can be used for mixed states, simply by providing an incomplete tableau. In that case it is possible to attempt to project on an operator that can not be generated by the provided stabilizer operators. In that case we have anticom_index==rank
and result===nothing
, where rank
is the the new rank of the tableau, one more than the number of rows in the initial tableau. However, if keep_result
was set to false
, then anticom_index
would stay at zero.
julia> s = S"XZI
+ IZI";
+
+
+julia> project!(s, P"IIX")[1]
++ X__
++ _Z_
If we had used MixedStabilizer
we would have added the projector to the list of stabilizers.
julia> s = one(MixedStabilizer, 2, 3)
++ Z__
++ _Z_
+
+julia> project!(s, P"IIX")[1]
++ Z__
++ _Z_
++ __X
However, MixedDestabilizer
would be an even better choice as it has $\mathcal{O}(n^2)$ complexity instead of the $\mathcal{O}(n^3)$ complexity of *Stabilizer
.
julia> s = one(MixedDestabilizer, 2, 3)
+𝒟ℯ𝓈𝓉𝒶𝒷
++ X__
++ _X_
+𝒳ₗ━━━
++ __X
+𝒮𝓉𝒶𝒷━
++ Z__
++ _Z_
+𝒵ₗ━━━
++ __Z
+
+julia> project!(s, P"IIX")[1]
+𝒟ℯ𝓈𝓉𝒶𝒷
++ X__
++ _X_
++ __Z
+𝒮𝓉𝒶𝒷━
++ Z__
++ _Z_
++ __X
See the "Datastructure Choice" section in the documentation for more details.
See also: projectX!
, projectY!
, projectZ!
, projectrand!
QuantumInterface.project!
— Methodproject!(
+ state::MixedStabilizer,
+ pauli::PauliOperator;
+ phases
+) -> Tuple{MixedStabilizer, Any, Any}
+
When using project!
on MixedStabilizer
it automates some of the extra steps we encounter when implicitly using the Stabilizer
datastructure to represent mixed states. Namely, it helps when the projector is not among the list of stabilizers:
julia> s = S"XZI
+ IZI";
+
+
+julia> ms = MixedStabilizer(s)
++ X__
++ _Z_
+
+julia> project!(ms, P"IIY")[1]
++ X__
++ _Z_
++ __Y
Similarly to project!
on Stabilizer
, this function has cubic complexity when the Pauli operator commutes with all rows of the tableau. Most of the time it is better to simply use MixedDestabilizer
representation.
Unlike other project!
methods, this one does not allow for keep_result=false
, as the correct rank or anticommutation index can not be calculated without the expensive (cubic) canonicalization operation required by keep_result=true
.
See the "Datastructure Choice" section in the documentation for more details.
QuantumInterface.projectX!
— MethodMeasure a given qubit in the X basis. A faster special-case version of project!
.
See also: project!
, projectXrand!
, projectY!
, projectZ!
.
QuantumInterface.projectY!
— MethodMeasure a given qubit in the Y basis. A faster special-case version of project!
.
See also: project!
, projectYrand!
, projectX!
, projectZ!
.
QuantumInterface.projectZ!
— MethodMeasure a given qubit in the Z basis. A faster special-case version of project!
.
See also: project!
, projectZrand!
, projectY!
, projectX!
.
QuantumInterface.reset_qubits!
— Methodreset_qubits!(
+ s::MixedDestabilizer,
+ newstate::QuantumClifford.AbstractStabilizer,
+ qubits;
+ phases
+) -> MixedDestabilizer
+
QuantumInterface.reset_qubits!
— Methodreset_qubits!(
+ s::MixedStabilizer,
+ newstate,
+ qubits;
+ phases
+) -> MixedStabilizer
+
QuantumInterface.reset_qubits!
— Methodreset_qubits!(
+ s::Stabilizer,
+ newstate,
+ qubits;
+ phases
+) -> Union{PauliOperator, Stabilizer}
+
Reset a given set of qubits to be in the state newstate
. These qubits are traced out first, which could lead to "nonlocal" changes in the tableau.
QuantumInterface.tensor
— FunctionTensor product between operators or tableaux.
Tensor product between CiffordOperators:
julia> tensor(CliffordOperator(sCNOT), CliffordOperator(sCNOT))
+X₁ ⟼ + XX__
+X₂ ⟼ + _X__
+X₃ ⟼ + __XX
+X₄ ⟼ + ___X
+Z₁ ⟼ + Z___
+Z₂ ⟼ + ZZ__
+Z₃ ⟼ + __Z_
+Z₄ ⟼ + __ZZ
Tensor product between PauliOperators:
julia> tensor(P"-IXYZ", P"iIXYZ")
+-i_XYZ_XYZ
Tensor product between Tableaux:
julia> s = S"-XX
+ +ZZ";
+
+julia> tensor(s, s, s)
+- XX____
++ ZZ____
+- __XX__
++ __ZZ__
+- ____XX
++ ____ZZ
+
+julia> s = S"+XZI
+ -IZI";
+
+julia> tensor(s, s)
++ XZ____
+- _Z____
++ ___XZ_
+- ____Z_
See also tensor_pow
.
QuantumInterface.tensor_pow
— MethodRepeated tensor product of an operators or a tableau.
For CliffordOperator
:
julia> tensor_pow(CliffordOperator(sHadamard), 3)
+X₁ ⟼ + Z__
+X₂ ⟼ + _Z_
+X₃ ⟼ + __Z
+Z₁ ⟼ + X__
+Z₂ ⟼ + _X_
+Z₃ ⟼ + __X
For PauliOperator
:
julia> tensor_pow(P"IXYZ", 2)
++ _XYZ_XYZ
For Tableaux
:
julia> tensor_pow(S"Z", 4)
++ Z___
++ _Z__
++ __Z_
++ ___Z
+
+julia> s = S"+XZI
+ +IZI";
+
+julia> tensor_pow(s, 3)
++ XZ_______
++ _Z_______
++ ___XZ____
++ ____Z____
++ ______XZ_
++ _______Z_
See also tensor
.
QuantumInterface.traceout!
— Methodtraceout!(
+ s::Stabilizer,
+ qubits;
+ phases,
+ rank
+) -> Union{Tuple{Stabilizer, Any}, Stabilizer}
+
Trace out a qubit.
See also: delete_columns
QuantumInterface.traceout!
— Methodtraceout!(
+ s::Union{MixedDestabilizer, MixedStabilizer},
+ qubits;
+ phases,
+ rank
+) -> Union{Tuple{Union{MixedDestabilizer, MixedStabilizer}, Any}, MixedDestabilizer, MixedStabilizer}
+
Private API
These functions are used internally by the library and might be drastically modified or deleted without warning or deprecation.
QuantumClifford.AbstractMeasurement
— TypeSupertype of all symbolic single-qubit measurements.
QuantumClifford.SymbolicDataType
— TypeAn intermediary when we want to create a new concrete type in a macro.
QuantumClifford.Tableau
— TypeInternal Tableau type for storing a list of Pauli operators in a compact form. No special semantic meaning is attached to this type, it is just a convenient way to store a list of Pauli operators. E.g. it is not used to represent a stabilizer state, or a stabilizer group, or a Clifford circuit.
Base.hcat
— MethodBase.inv
— Methodinv(
+ c::CliffordOperator;
+ phases
+) -> CliffordOperator{QuantumClifford.Tableau{Vector{UInt8}, Matrix{UInt64}}}
+
Inverse of a CliffordOperator
julia> inv(CliffordOperator(sCNOT))
+X₁ ⟼ + XX
+X₂ ⟼ + _X
+Z₁ ⟼ + Z_
+Z₂ ⟼ + ZZ
+
+julia> inv(CliffordOperator(sCNOT(2, 1), 2))
+X₁ ⟼ + X_
+X₂ ⟼ + XX
+Z₁ ⟼ + ZZ
+Z₂ ⟼ + _Z
+
+julia> inv(CliffordOperator(tHadamard))
+X₁ ⟼ + Z
+Z₁ ⟼ + X
Base.permute!
— MethodPermute the qubits (i.e., columns) of the state in place.
Base.permute!
— MethodPermute the qubits (i.e., columns) of the tableau in place.
Base.vcat
— MethodLinearAlgebra.dot
— MethodThe inner product of two Stabilizers.
Based on (Garcia et al., 2012).
julia> using LinearAlgebra
+
+julia> dot(S"Z", S"Z")
+1.0
+
+julia> dot(S"Z", S"Y")
+0.7071067811865476
See also: logdot
QuantumClifford._apply_nonthread!
— MethodNonvectorized version of apply!
used for unit tests.
QuantumClifford._apply_nonthread!
— MethodNonvectorized version of apply!
used for unit tests.
QuantumClifford._mul_left_nonvec!
— MethodNonvectorized version of mul_left!
used for unit tests.
QuantumClifford._remove_rowcol!
— MethodUnexported low-level function that removes a row (by shifting all rows up as necessary)
Because MixedDestabilizer is not mutable we return a new MixedDestabilizer with the same (modified) xzs array.
Used on its own, this function will break invariants. Meant to be used with projectremove!
.
QuantumClifford._rowmove!
— MethodUnexported low-level function that moves row i to row j.
Used on its own, this function will break invariants. Meant to be used in _remove_rowcol!
.
QuantumClifford._stim_prodphase
— MethodThe quantumlib/Stim implementation, which performs the prodphase and mul_left! together. Used for unit tests.
QuantumClifford.apply_single_x!
— MethodApply a Pauli X to the i
-th qubit of state s
. You should use apply!(stab,sX(i))
instead of this.
QuantumClifford.apply_single_y!
— MethodApply a Pauli Y to the i
-th qubit of state s
. You should use apply!(stab,sY(i))
instead of this.
QuantumClifford.apply_single_z!
— MethodApply a Pauli Z to the i
-th qubit of state s
. You should use apply!(stab,sZ(i))
instead of this.
QuantumClifford.applynoise_branches
— FunctionCompute all possible new states after the application of the given noise model. Reports the probability of each one of them. Deterministic (as it reports all branches of potentially random processes), part of the Noise interface.
QuantumClifford.colswap!
— MethodSwap two columns of a stabilizer in place.
QuantumClifford.destab_looks_good
— MethodCheck basic consistency requirements of a destabilizer. Used in tests.
QuantumClifford.enumerate_cliffords_slow
— MethodThe O(n^4) implementation from (Koenig and Smolin, 2014) – their algorithm seems wrong as ⟨w'₁|wₗ⟩=bₗ which is not always zero.
QuantumClifford.fill_tril
— MethodAssign (symmetric) random ints to off diagonals of matrix.
QuantumClifford.get_all_subtypes
— MethodReturns a tuple of all concrete subtypes and all UnionAll non-abstract subtypes of a given type.
QuantumClifford.gf2_H_standard_form_indices
— MethodThe permutation of columns which turns a binary matrix into standard form. It is assumed the matrix has already undergone Gaussian elimination.
QuantumClifford.initZ!
— MethodinitZ!(frame::PauliFrame) -> PauliFrame
+
Inject random Z errors over all frames and qubits for the supplied PauliFrame with probability 0.5.
Calling this after initialization is essential for simulating any non-deterministic circuit. It is done automatically by most PauliFrame
constructors.
QuantumClifford.make_sumtype
— Methodjulia> make_sumtype([sCNOT])
+quote
+ @sum_type CompactifiedGate :hidden begin
+ sCNOT(::Int64, ::Int64)
+ end
+end
QuantumClifford.make_sumtype_method
— Function``` julia> makesumtypemethod([sCNOT], :apply!, (:s,)) quote function QuantumClifford.apply!(s, g::CompactifiedGate) @cases g begin sCNOT(q1, q2) => apply!(s, sCNOT(q1, q2)) end end end
QuantumClifford.make_sumtype_variant_constructor
— Methodjulia> make_sumtype_variant_constructor(sCNOT)
+:(CompactifiedGate(g::sCNOT) = begin
+ (CompactifiedGate').sCNOT(g.q1, g.q2)
+end)
QuantumClifford.make_variant
— Methodjulia> make_variant(sCNOT)
+:(sCNOT(::Int64, ::Int64))
QuantumClifford.make_variant_deconstruct
— Functionjulia> make_variant_deconstruct(sCNOT, :apply!, (:s,))
+:(sCNOT(q1, q2) => apply!(s, sCNOT(q1, q2)))
QuantumClifford.mixed_destab_looks_good
— MethodCheck basic consistency requirements of a mixed destabilizer. Used in tests.
QuantumClifford.mixed_stab_looks_good
— MethodCheck basic consistency requirements of a mixed stabilizer. Used in tests.
QuantumClifford.pfmeasurement
— FunctionFor a given simulated state, e.g. a PauliFrame
instance, returns the measurement results.
QuantumClifford.precise_inv
— MethodInverting a binary matrix: uses floating point for small matrices and Nemo for large matrices.
QuantumClifford.project_cond!
— MethodQuantumClifford.projectremoverand!
— MethodUnexported low-level function that projects a qubit and returns the result while making the tableau smaller by a qubit.
Because MixedDestabilizer is not mutable we return a new MixedDestabilizer with the same (modified) xzs array.
QuantumClifford.quantum_mallows
— MethodSample (h, S) from the distribution P_n(h, S) from Bravyi and Maslov Algorithm 1.
QuantumClifford.remove_column!
— MethodUnexported low-level function that removes a column (by shifting all columns to the right of the target by one step to the left)
Because Tableau is not mutable we return a new Tableau with the same (modified) xzs array.
QuantumClifford.rowdecompose
— MethodDecompose a Pauli $P$ in terms of stabilizer and destabilizer rows from a given tableaux.
For given tableaux of rows destabilizer rows $\{d_i\}$ and stabilizer rows $\{s_i\}$, there are boolean vectors $b$ and $c$ such that $P = i^p \prod_i d_i^{b_i} \prod_i s_i^{c_i}$.
This function returns p
, b
, c
.
julia> s = MixedDestabilizer(ghz(2))
+𝒟ℯ𝓈𝓉𝒶𝒷
++ Z_
++ _X
+𝒮𝓉𝒶𝒷
++ XX
++ ZZ
+
+julia> phase, destab_rows, stab_rows = QuantumClifford.rowdecompose(P"XY", s)
+(3, Bool[1, 0], Bool[1, 1])
+
+julia> im^3 * P"Z_" * P"XX" * P"ZZ"
++ XY
QuantumClifford.sample_geometric_2
— MethodThis function samples a number from 1 to n
where n >= 1
probability of outputting i
is proportional to 2^i
QuantumClifford.stab_looks_good
— MethodCheck basic consistency requirements of a stabilizer. Used in tests.
QuantumClifford.to_cpu
— Functioncopies the memory content of the object to CPU
You can only use this function if CUDA.jl is imported
For more advanced users to_cpu(data, element_type)
will reinterpret elements of data and converts them to element_type
. For example based on your CPU architecture, if working with matrices of UInt32 is faster than UInt64, you can use to_cpu(data, UInt32)
julia> using QuantumClifford: to_cpu, to_gpu
+
+julia> using CUDA # without this import, to_cpu, to_gpu are just function
+
+julia> stab = S"- X_Z\n+ _ZZ\n+ __Z"
+- X_Z
++ _ZZ
++ __Z
+
+julia> stab_gpu = to_gpu(stab);
+
+julia> apply!(stab_gpu, sHadamard(1));
+
+julia> stab_result_cpu = to_cpu(stab_gpu)
+- Z_Z
++ _ZZ
++ __Z
julia> using QuantumClifford: to_cpu, to_gpu
+
+julia> using CUDA # without this import, to_cpu, to_gpu are just function
+
+julia> pf_gpu = to_gpu(PauliFrame(1000, 2, 2));
+julia> circuit = [sMZ(1, 1), sHadamard(2), sMZ(2, 2)];
+julia> pftrajectories(pf_gpu, circuit);
+julia> measurements = to_cpu(pf_gpu.measurements);
See also: to_gpu
QuantumClifford.to_gpu
— Functioncopies the memory content of the object to GPU
You can only use this function if CUDA.jl is imported
For more advanced users to_gpu(data, element_type)
will reinterpret elements of data and converts them to element_type
. For example based on your GPU architecture, if working with matrices of UInt64 is faster than UInt32, you can use to_gpu(data, UInt64)
julia> using QuantumClifford: to_cpu, to_gpu
+
+julia> using CUDA # without this import, to_cpu, to_gpu are just function
+
+julia> stab = S"- X_Z\n+ _ZZ\n+ __Z"
+- X_Z
++ _ZZ
++ __Z
+
+julia> stab_gpu = to_gpu(stab);
+
+julia> apply!(stab_gpu, sHadamard(1));
+
+julia> stab_result_cpu = to_cpu(stab_gpu)
+- Z_Z
++ _ZZ
++ __Z
julia> using QuantumClifford: to_cpu, to_gpu
+
+julia> using CUDA # without this import, to_cpu, to_gpu are just function
+
+julia> pf_gpu = to_gpu(PauliFrame(1000, 2, 2));
+julia> circuit = [sMZ(1, 1), sHadamard(2), sMZ(2, 2)];
+julia> pftrajectories(pf_gpu, circuit);
+julia> measurements = to_cpu(pf_gpu.measurements);
See also: to_cpu
QuantumClifford.trusted_rank
— FunctionA "trusted" rank
which returns rank(state)
for Mixed[De]Stabilizer
and length(state)
for [De]Stabilizer
.
QuantumClifford.zero!
— MethodZero-out a given row of a Tableau
QuantumClifford.zero!
— MethodZero-out the phases and single-qubit operators in a PauliOperator
QuantumClifford.@qubitop1
— MacroMacro used to define single qubit symbolic gates and their qubit_kernel
methods.
QuantumClifford.@qubitop2
— MacroMacro used to define 2-qubit symbolic gates and their qubit_kernel
methods.
QuantumClifford.@valbooldispatch
— MacroTurns f(Val(x))
into x ? f(Val(true)) : f(Val(false))
in order to avoid dynamic dispatch