Error Generators¶
Error models are represented by classes called “error generators” that are in the error_gens
namespace. They are called upon by circuit_runners
to apply noise to ideal quantum circuits.
In this section I will discuss GatewiseGen
and DepolarGen
classes. Both represent stochastic error models
. That is, error models that apply gates as noise according to classical probability distributions.
GatewiseGen¶
The GatewiseGen
class allow one to define custom stochastic error-models where for each ideal gate-type the errors applied to the ideal gate and the classical probability distribution for applying errors can be specified. Since many examples of using the class are given, I have moved the discussion of the GatewiseGen
class to Gate-wise Error Models.
The follow section provides examples of how error_gens
are used in practice
DepolarGen¶
The DepolarGen
class is used to represent the symmetric depolarizing channel, which is commonly studied in QEC. For single-qubit gates, this class is used to apply errors at probability \(p\) from set \(\{X, Y, Z\}\). For two-qubit gates, errors also occur with probability \(p\) but errors are chosen uniformally from the set \(\{I, X, Y, Z\}^{\otimes 2} \; \setminus \; I\otimes I\). Errors are always applied after ideal gates except for measurements. In which case, the errors are applied before.
An example of creating an instance of DepolarGen
is seen here:
>>> import pecos as pc
>>> depolar = pc.error_gens.DepolarGen(model_level='code_capacity', has_idle_errors=False, perp_errors=True)
The model_level
keyword is used to specify to what set of gates the DepolarGen
is applied to. If model_level
is set to the value of 'code\_capacity'
, then the error model is applied before each LogicalInstruction
to each data qubits as if these qubits are acted on by 'I'
. The error model is not applied to any other circuit element. If model_level
is set to the value 'phenomenological'
, then the error model applied to data qubits before each LogicalInstruction
as well as to any measurement. If model_level
is set to the value 'circuit'
, then the error model is applied to all the gates in the QuantumCircuit
. The default value of model_level
is 'circuit'
.
The has_idle_errors
is a keyword that is only relevant when model_level == 'circuit'
. If has_idle_errors
is set to True
, then the error model is applied to inactive qubits as if the qubit is acted on by 'I'
. If has_idle_errors
is set to False
, then this does not occur. The default value of has_idle_errors
is True
.
If the perp_errors
keyword is set to True
, then errors that are applied to Pauli-basis initializations and measurements are errors that do not include the Pauli-basis of the initializations or measurements. So, for example, \(Z\) is not applied as an error to the 'init |0>'
operation. If the perp_errors
keyword is set to False
, then there is no restriction to the errors. The default value of perp_errors
is True
.
An example of applying an error model using DepolarGen
to a LogicalCircuit
is seen in the following:
>>> depolar = pc.error_gens.DepolarGen(model_level='code_capacity')
>>> surface = pc.qeccs.Surface4444(distance=3)
>>> logic = pc.circuits.LogicalCircuit()
>>> logic.append(surface.gate('ideal init |0>'))
>>> logic.append(surface.gate('I'))
>>> circ_runner = pc.circuit_runners.Standard(seed=1)
>>> state = circ_runner.init(surface.num_qudits)
>>> meas, err = circ_runner.run_logic(state, logic, error_gen=depolar, error_params={'p': 0.1})
Note that the keyword argument error_params
is used to pass a dictionary that indicates the probability \(p\) of the depolarizing error model.
The values returned by the run_logic
method is recorded in the variables meas
and err
. These variables are dictionaries that record the measurement output and applied errors.
An example of measurement outcomes is given here:
>>> # Following the previous example.
>>> meas
{(1, 0): {7: {9: 1, 11: 1}}}
Here, in the last line, we see the measurement outcome. The key of the outer dictionary is a tuple where the first element is the tick index of the LogicalGate
and the second element is an index corresponding to a LogicalInstance
. That is, the tuple records at what point in the LogicalCircuit
was the measurement made. The value of the outer dictionary is just the measurement-outcome dictionary of a QuantumCircuit
.
We can see the errors that were generated by the DepolarGen
in these lines:
>>> # Following the previous example.
>>> err
{(1, 0): {0: {'after': QuantumCircuit([{'X': {4}, 'Z': {10}}])}}}
In the above Listing, we see a dictionary that stores what errors were applied to the LogicalCircuit
. The key of the outer dictionary, once again, is a tuple indicating the tick of a LogicalGate
and the index of a LogicalInstance
. The key of the next inner dictionary is QuantumCircuit
tick when the error occurred. The key 'after'
of the next inner dictionary indicates that the errors are applied after ideal gates. The key 'before'
is used when indicating that errors are applied before gates. The values of both the 'after'
and 'before'
keys are QuantumCircuits
. These circuits are the errors that are applied.
The data structure used to describe the errors that are applied to a LogicalCircuit
can be directly supplied to a run_logic
method of a circuit_runner
. Doing so will cause the run_logic
method to apply the given error to a LogicalCircuit
. This can be seen in the following:
>>> # Continuing the previous examples.
>>> logic2 = pc.circuits.LogicalCircuit()
>>> logic2.append(surface.gate('ideal init |+>'))
>>> logic2.append(surface.gate('I'))
>>> state2 = circ_runner.init(surface.num_qudits)
>>> meas2, err2 = circ_runner.run_logic(state2, logic2, error_circuits=err)
One use for this is to apply the same error to a different logical basis-state. Doing so allows one to determine if a logical error occurs for the logical operations that stabilizer the basis state.
Note that the circuit_runners
currently only apply errors to LogicalCircuits
and not to QuantumCircuits
.
Todo
Discuss the leakage error model when it is verified…