# 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¶

`run` |
Combines a `circuit` , `error_gen` , `simulator` to run a simulation. |

### 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()
```

The `run`

method is used to apply a `QuantumCircuit`

or some other circuit instance to a state, where a state is an
instance of a simulator (see Simulators). This is seen in the following:

```
>>> state = pc.simulators.SparseSim(num_qubits=4)
>>> qc = pc.circuits.QuantumCircuit()
>>> qc.append('X', {0, 1})
>>> qc.append('measure Z', {0, 1, 3})
>>> circ_runner.run(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 = pc.simulators.SparseSim(surface.num_qudits)
>>> circ_runner.run(state, logic)
({}, {})
```

The final line is the output of `run`

. 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`

.

## 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 = pc.simulators.SparseSim(4)
>>> qc = pc.circuits.QuantumCircuit()
>>> qc.append('X', {0, 1, 2, 3})
>>> circ_runner.run(state, qc)
({}, {})
>>> circ_runner.total_time # doctest: +SKIP
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 code block.
>>> circ_runner.reset_time()
>>> circ_runner.total_time
0.0
```