# 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