# Built-In Simulator¶

After defining a circuit or generating a circuit collection, the next step is to
run it on a simulator or quantum device. For ease of implementation, the
True-Qᵀᴹ SDK includes a built-in `Simulator`

that can
be configured with custom or pre-defined noise models.

- Generate circuits
The first step, before using a

`Simulator`

to simulate a`Circuit`

or`CircuitCollection`

, is to define the ideal version of the circuit(s) to be run. In this example, we will write a custom`Circuit`

with an X gate acting on the \(0^{th}\) qubit, followed by a controlled-Z gate on qubits 1 and 2 at the same time as a Y gate acts on the \(0^{th}\) qubit:import tq as trueq circuit = tq.Circuit(tq.Cycle({(0, ):tq.Gate.x}), tq.Cycle({(0, ): tq.Gate.y, (1, 2):tq.Gate.cz}))

- Initialize a Simulator
The next step is to initialize a

`Simulator`

, and (optionally) add noise. In this example we will not add noise; for a noisy implementation, see the example below. To initialize a simulator, run:sim=tq.Simulator()

- Add Noise (Optional)
To simulate a noisy implementation of a

`Circuit`

or`CircuitCollection`

, a noise process should be added to the simulator prior to running the circuit(s). The following code adds depolarizing noise to the`Simulator`

initialized in step 2.sim.add_depolarizing(p = 0.001)

See Noise for details about the True-Qᵀᴹ SDK suite of noise models.

- Add an initial state (Optional)
By default, every qubit will initialize in the \(|0\rangle\)state. To prepare a different initial state, use

`add_prep()`

. An initial state can be specified by a \(d\times d\) density matrix or a length \(d\) vector representing a pure state. The following code specifies the initial state of qubit 1.sim.add_prep({1: [[0.8, 0.1], [0.1, 0.2]]})

- Run the Simulation
The final step is to run the

`Circuit`

or`CircuitCollection`

on the simulator. The following line of code runs the simulation and accordingly populates the`results`

of the circuit with 100 random shots.sim.run(circuit, n_shots = 100)

## Noise¶

The True-Qᵀᴹ SDK includes several functions for adding noise to a
`Simulator`

, including several common noise models and the option to
define a custom noise process using Kraus operators. Every function below causes
the specified noise model to act at every location where a circuit acts in a simulator.

- Depolarizing
The

`add_depolarizing`

function adds depolarizing noise to a simulator, so that when a circuit is run on the simulator, depolarizing noise acts independently and isotropically on every location in the circuit. The following code initializes a`Simulator`

with depolarizing noise with depolarizing parameter \(p = 0.03\):sim = tq.Simulator().add_depolarizing(p = 0.03)

- Stochastic Pauli
The

`add_stochastic_pauli`

function adds a stochastic pauli noise to a simulator. The following code initializes a simulator with stochastic pauli noise, where the probability of an \(X\), \(Y\), and \(Z\) error are \(px = 0.01\), \(py = 0.02\), and \(pz = 0.03\), respectively:sim = tq.Simulator().add_stochastic_pauli(px = 0.01, py = 0.02, pz = 0.03)

- Overrotation
The

`add_overrotation`

function adds an overrotation error at every location where a single qubit or multi-qubit gate acts in a simulator. The angle of overrotation can be specified separately for single and multi-qubit gates. The following code initializes a`Simulator`

with an overrotation by \(\epsilon=0.01\) to single qubit gates and \(\epsilon=0.05\) to multi-qubit gates:sim = tq.Simulator().add_overrotation(single_sys = 0.01, multi_sys = 0.05)

- Custom Kraus
The

`add_kraus`

allows users to add custom noise to a simulator, specified by Kraus operators. The following code initializes a`Simulator`

with amplitude damping noise constructed from Kraus operators, with a decay probability of \(p = 0.01\):import numpy as np p = 0.01 kraus_ops = [np.sqrt(np.array([[0, p], [0, 0]])), np.sqrt(np.diag([1, 1-p]))] sim = tq.Simulator().add_kraus(kraus_ops)

- State preparation errors
The

`add_prep`

method customizes the preparation of quantum registers. This can be used to set preparations to a specific pure or mixed state, or to add simple bitflip error.# add 1% bitflip error by default, but 6% for qubits 2 and 3 sim = tq.Simulator().add_prep({None: 0.01, 2: 0.06, 3: 0.06}) # add specific density matrix to qubit 1, but 1% bitflip error by default sim = tq.Simulator().add_prep({None: 0.01, 1: [[0.8, 0.1], [0.1, 0.2]]}) # prepare all qubits in the |+> state sim = tq.Simulator().add_prep([1 / np.sqrt(2), 1 / np.sqrt(2)])

- Measurement errors
The

`trueq.Simulator.add_readout_error`

method allows users to add measurement errors to a simulator.# all qubits get a 0.5% readout error sim = tq.Simulator().add_readout_error(0.005) # all qubits get a 1% readout error, except qubit 1 gets a 3% readout error sim = tq.Simulator().add_readout_error({None: 0.01, 1: 0.03}) # all qubits get a 1% readout error, except qubit 1 gets a 4% readout error, and # qubit 3 gets an asymmetric readout error of 1% on |0> and 8% on |1> sim = tq.Simulator().add_readout_error({None: 0.01, 1: 0.04, 3: [0.01, 0.08]})

## Example¶

```
#
# Noisy simulator example.
# Copyright 2020 Quantum Benchmark Inc.
#
import trueq as tq
import numpy as np
# Define the circuit we are interested in
circuit = tq.Circuit({0: tq.Gate.x, 1: tq.Gate.y}, {(0, 1): tq.Gate.cz})
circuit.measure_all()
# Initialize a noiseless simulator.
sim0 = tq.Simulator()
# Initialize a simulator with depolarizing noise at 8% per qubit.
sim1 = tq.Simulator().add_depolarizing(p=0.08)
# Initialize a simulator with pauli noise and overrotation.
sim2 = (
tq.Simulator()
.add_stochastic_pauli(px=0.01, py=0.02)
.add_overrotation(single_sys=0.04)
)
# Initialize a simulator with dephasing noise.
kraus_ops = [np.sqrt(0.99) * np.eye(2), np.sqrt(0.01) * np.diag([1, -1])]
sim3 = tq.Simulator().add_kraus(kraus_ops)
# Initialize a simulator with state preparation and measurement errors
sim4 = tq.Simulator().add_prep({None: 0.03}).add_readout_error(0.01)
# Run circuit on each of the simulators above and print the results.
sim0.run(circuit)
print(f"{'Ideal':>28}:", circuit.results)
sim1.run(circuit, overwrite=True)
print(f"{'Depolarizing':>28}:", circuit.results)
sim2.run(circuit, overwrite=True)
print(f"{'Stochastic w/ overrotation':>28}:", circuit.results)
sim3.run(circuit, overwrite=True)
print(f"{'Dephasing':>28}:", circuit.results)
sim4.run(circuit, overwrite=True)
print(f"{'SPAM Error':>28}:", circuit.results)
```

This produces the following output:

```
Ideal: Results({'11': 50})
Depolarizing: Results({'01': 6, '10': 1, '11': 43})
Stochastic w/ overrotation: Results({'01': 2, '10': 3, '11': 45})
Dephasing: Results({'11': 50})
SPAM Error: Results({'11': 50})
```

## Example¶

The built-in simulator has functions which allow users to retrieve the output state after a circuit has run, and to retrieve the operator representation of a circuit. The below example demonstrates these functions.

```
#
# Noisy simulator example 2.
# Copyright 2020 Quantum Benchmark Inc.
#
import trueq as tq
# Define a circuit that we are interested in
circuit = tq.Circuit({0: tq.Gate.y, 1: tq.Gate.z}, {(0, 1): tq.Gate.cx})
# Compute the unitary matrix of the circuit by using a noiseless simulator
print("Unitary:\n", tq.Simulator().operator(circuit).mat(), "\n")
# Compute the final pure state in the case of no noise
print("Final pure state:\n", tq.Simulator().state(circuit).mat(), "\n")
```

```
Unitary:
[[ 0.+0.j 0.+0.j 0.-1.j 0.+0.j]
[ 0.+0.j -0.+0.j 0.+0.j 0.+1.j]
[ 0.+1.j 0.+0.j 0.+0.j 0.+0.j]
[ 0.+0.j -0.-1.j 0.+0.j -0.+0.j]]
Final pure state:
[0.+0.j 0.+0.j 0.+1.j 0.+0.j]
```