# Simulator¶

True-Q™ includes a built-in `Simulator`

that can be configured with
custom or pre-defined noise models. Noise models can define gate and cycle noise,
as well as preparation and measurement noise. A simulator object can be used to:

Simulate the final quantum state of a circuit. This will be a pure state vector if the noise model is ideal or has only unitary noise, and will be a density matrix otherwise.

Simulate the total effective operator of a circuit. This will be a unitary matrix if the noise model is ideal or has only unitary noise, and will be a superoperator otherwise.

Sample a given number of bitstrings (or ditstrings if higher energy levels are defined) from the distribution defined by the final simulated quantum state of a circuit. These results can be returned, or can be automatically populated into the respective

`results`

attributes of the given circuits.

## Noise Models¶

A `Simulator`

instance owns an ordered list of noise models. There are
a number of built-in noise models that can be chained
together in a single simulator instance as follows:

```
import trueq as tq
# make a simulator that first overrotates single qubit gates by 0.03, and then
# adds stochastic error in the X direction
sim = tq.Simulator().add_overrotation(0.03).add_stochastic_pauli(px=0.02)
```

Demonstrations of adding custom noise models can be found in the examples at the bottom
of this page. A noise model is defined as a function that accepts a
`Cycle`

to simulate and the current state of simulation, and mutates
the current state of simulation based on the cycle. Here, the “state of simulation” is a
generalized notion of state and can refer to either:

A

`StateTensor`

which can store either a pure state or a mixed state. Anytime it represents a pure state and a noise model asks it to update by a non-unitary operation, it casts itself into a mixed state.A

`OperatorTensor`

which can store either a unitary or a superoperator. Anytime it represents a unitary and a noise model asks it to update itself by a non-unitary operation, it casts itself into a superoperator.

**During the simulation of a circuit, the simulator iterates through cycles and passes
each cycle to each noise model in turn**. The order is defined by the order in which
they are added to the simulator, with two exceptions:

Preparation noise takes place before any other noise, and a simulator can only have one source of preparation noise (noise is flagged as being preparation noise when it is added to the simulator, usually under the hood).

Measurement noise happens after any other noise source. Note that measurements are currently only supported in the last cycle of a circuit.

## Advanced Note¶

The following note is included for advanced users of the simulator and for the benefit of users who are interested in writing their own noise model.

Every simulator has one special noise source whose job is to attempt to actually
simulate the cycle at hand. Indeed, if no noise sources tried to simulate the gates, it
would not really be a simulation of the circuit, and if more than one of the noise
sources tried to simulate the circuit, the end result would be quite incorrect.
Therefore, every noise source defines a flag that specifies whether it is the kind of
noise source that tries to *actually implement the cycles* it finds, as opposed to *only
injecting noise*. For example, `add_overrotation()`

sets this
flag to `True`

because it implements gates as \(U^{1+\epsilon}\) which is an
attempt to actually implement \(U\), but
`add_depolarizing()`

sets this flag to `False`

because it just
adds a small perturbation to the state or superoperator during each cycle.

*If no noise sources in a simulator have this flag as true* then an “ideal noise
source” is automatically *prepended* to the noise list. This “noise source” simulates
the gates it sees and adds no noise. Therefore, for example, if it is of interest to
add a noise source that precedes the cycle in question (rather than the default of
following the cycle in question), then an ideal noise source could be manually added
to the simulator after the actual noise source.

```
import trueq as tq
import warnings
# If more than one noise source is added whose flag is true, a warning is raised, as
# in the following example.
bad_sim = tq.Simulator()
with warnings.catch_warnings():
warnings.simplefilter("ignore")
bad_sim.add_overrotation(0.01, noisy_gates=tq.Gate.x)
bad_sim.add_overrotation(0.02, noisy_gates=tq.Gate.y)
# In the example above, it happens to be easy to get around the problem by noting
# that we can use Kraus maps with one unitary operator as overrotation noise
good_sim = tq.Simulator()
good_sim.add_kraus([(tq.Gate.x ** 0.01).mat], noisy_gates=tq.Gate.x)
good_sim.add_kraus([(tq.Gate.y ** 0.02).mat], noisy_gates=tq.Gate.y)
```

```
<trueq.simulation.simulator.Simulator at 0x7fdc37ad4160>
```