Operators in Circuit Simulations
This is experimental functionality with an unstable API.
Import with using QuantumClifford.Experimental.NoisyCircuits
.
Too see a condensed list of all operations check out the API docs.
Unitary Gates
We distinguish between symbolic gates like sCNOT
that have specialized (fast) apply!
methods (usually just for single and two qubit gates) and general tableau representation of gates like CliffordOperator
that can represent any multi-qubit gate.
Predefined unitary gates are available, like sCNOT
, sHadamard
, etc.
[sCNOT(2,4),sHadamard(2),sCPHASE(1,3),sSWAP(2,4)]
Any arbitrary tableaux can be used as a gate too.
They can be specified by giving a Clifford operator tableaux and the indices on which it acts (particularly useful for gates acting on a small part of a circuit):
SparseGate(tCNOT, [2,4])
The Clifford operator tableaux can be completely arbitrary.
SparseGate(random_clifford(3), [2,4,5])
If the Clifford operator acts on all qubits, we do not need to specify indices, just use the operator.
Noisy Gates
Each gate can be followed by noise applied to the qubits on which it has acted. This is done by wrapping the given gate into a NoisyGate
ε = 0.03 # X/Y/Z error probability
noise = UnbiasedUncorrelatedNoise(ε)
noisy_gate = NoisyGate(SparseGate(tCNOT, [2,4]), noise)
In circuit diagrams the noise is not depicted, but after each application of the gate defined in noisy_gate
, a noise operator will also be applied. The example above is of Pauli Depolarization implemented by UnbiasedUncorrelatedNoise
.
One can also apply only the noise operator by using NoiseOp
which acts only on specified qubits. Or alternatively, one can use NoiseOpAll
in order to apply noise to all qubits.
[NoiseOp(noise, [4,5]), NoiseOpAll(noise)]
Coincidence Measurements
Global parity measurements involving single-qubit projections and classical communication are implemented with BellMeasurement
. One needs to specify the axes of measurement and the qubits being measured. If the parity is trivial, the circuit continues, if the parity is non-trivial, the circuit ends and reports a detected failure. This operator is frequently used in the simulation of entanglement purification.
BellMeasurement([sMX(1), sMY(3), sMZ(4)])
There is also NoisyBellMeasurement
that takes the bit-flip probability of a single-qubit measurement as a third argument.
Stabilizer Measurements
A measurement over one or more qubits can also be performed, e.g., a direct stabilizer measurement on multiple qubits without the use of ancillary qubits. When applied to multiple qubits, this differs from BellMeasurement
as it performs a single projection, unlike BellMeasurement
which performs a separate projection for every single qubit involved. This measurement is implemented in PauliMeasurement
which requires a Pauli operator on which to project and the index of the classical bit in which to store the result. Alternatively, there are sMX
, sMZ
, sMY
if you are measuring a single qubit.
[PauliMeasurement(P"XYZ", 1), sMZ(2, 2)]
TODO: SparseMeasurement, NoisyMeasurement
Verification Operations
At the end of many circuits one might want to check whether they performed correctly. The VerifyOp
operation corresponds to an unphysical perfect tomographic operation, checking whether the state of the qubits at the given indices is indeed what is expected. If it is, the operation reports a success, otherwise it reports an undetected error.
desired_state = random_stabilizer(5)
qubit_indices = [1,2,3,4,7]
VerifyOp(desired_state, qubit_indices)
Reset Operations
The Reset
operations lets you trace out the specified qubits and set their state to a specific tableau.
new_state = random_stabilizer(3)
qubit_indices = [1,2,3]
Reset(new_state, qubit_indices)
It can be done anywhere in a circuit, not just at the beginning.
Gates Conditioned on Classical Bits
ConditionalGate
is a conditional gate that performs one of two provided gates, depending on the value of a given classical bit.
DecisionGate
is a conditional gate that performs one of the supplied gates
, depending on the output of decisionfunction
applied to the entire classical bit register.
gate1 = SparseGate(tCNOT, [1,2])
gate2 = sCPHASE(1,2)
gate3 = SparseGate(tSWAP, [1,3])
cg = ConditionalGate(gate1, gate2, 2)
dg = DecisionGate([gate1,gate2,gate3], bit_register->1) # it will always perform gate1
[sMX(4,1), sMZ(5,2), cg, dg]
TODO: Split ConditionalGate
into quantum conditional and classical conditional