# Circuit Runners¶

Classes belonging to the `circuit_runners`

namespace apply the gates of `LogicalCircuits`

and `QuantumCircuits`

to states represented by simulators. `circuit_runners`

are also responsible for applying error models to quantum circuit; however, we will discus this in Error Generators.

The main `circuit_runner`

is simply called `Standard`

. There is another call `TimingRunner`

, which is essentially the same as `Standard`

except that it is used to time how long it takes simulators to apply gates and can be used to compare the runtime of simulators. I will now discuss these two `circuit_runners`

.

## Standard¶

For convenience, the following tabels list the attributes and methods of `Standard`

:

### Methods¶

`init` |
Adds a collection of gates to the end of `ticks` . |

`run_circuit` |
Applies gates from a `QuantumCircuit` . |

`run_logic` |
Applies gates from a `LogicalCircuit` . |

### Attributes¶

`seed` |
The integer used as a seed for random number generators |

### Instance¶

To create an instance of `Standard`

one can simply write:

```
>>> import pecos as pc
>>> circ_runner = pc.circuit_runners.Standard()
```

By default, a `Standard`

uses the `StabSim`

as a simulator. This can be changed as follows:

```
>>> from somepackage import MyCustomSim
>>> circ_runner = pc.circuit_runners.Standard(simulator=MyCustomSim)
```

The `init`

method is used to (re)initialize a `simulator`

instance. An example of using this method to create a four-qubit registry is seen here:

```
>>> # Following from the previous code block.
>>> circ_runner = pc.circuit_runners.Standard()
>>> state = circ_runner.init(4)
```

The `run_circuit`

method is used to apply a `QuantumCircuit`

to a state in the following:

```
>>> # Continuing with the previous code block.
>>> qc = pc.circuits.QuantumCircuit()
>>> qc.append('X', {0, 1})
>>> qc.append('measure Z', {0, 1, 3})
>>> circ_runner.run_circuit(state, qc)
{1: {0: 1, 1: 1}}
```

In the last line of this code block, we see the measurement record produced by the `circuit_runner`

. The keys of the outer dictionary are tick indices, while for the inner dictionary the keys are the indices of qubits with non-zero measurements and the values are the measurement results.

The `run_logic`

method is used to apply `LogicalCircuits`

:

```
>>> surface = pc.qeccs.Surface4444(distance=3)
>>> logic = pc.circuits.LogicalCircuit()
>>> logic.append(surface.gate('ideal init |0>'))
>>> logic.append(surface.gate('I'))
>>> state = circ_runner.init(surface.num_qudits)
>>> circ_runner.run_logic(state, logic)
({}, {})
```

The final line is the output of `run_logic`

. The first dictionary is a record measurement and the second is a record of the errors generated. In this example, all the measurement results are zero and we have not applied any error models. In Error Generators, there are examples of where this is not the case; therefore, refer to that section if you are curious about the output of `run_logic`

.

## TimingRunner¶

As mention, `TimingRunner`

is essentially the same as `Standard`

except the runtime for applying gates is recorded. The attribute `total_time`

stores this value and is used in the following:

```
>>> circ_runner = pc.circuit_runners.TimingRunner()
>>> state = circ_runner.init(4)
>>> qc = pc.circuits.QuantumCircuit()
>>> qc.append('X', {0, 1, 2, 3})
>>> circ_runner.run_circuit(state, qc)
{}
>>> circ_runner.total_time
7.22257152574457e-06
```

`TimingRunner`

times the execution of gates by using Python’s `perf_counter`

method. The time recorded by `total_time`

continues to accumulate until it is reset by the `reset_time`

method:

```
>>> # Continuing from previous Listing.
>>> circ_runner.reset_time()
>>> circ_runner.total_time
0.0
```