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

1. 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}))

2. 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()


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.

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

1. 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)

2. 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)

3. 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)

4. 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]))]


## Example¶

#
# Noisy simulator example.
# Copyright 2019 Quantum Benchmark Inc.
#

import trueq as tq
import numpy as np

# Define a custom circuit.
circuit = tq.Circuit(
tq.Cycle({(0,): tq.Gate.x, (1,): tq.Gate.y}), tq.Cycle({(0, 1): tq.Gate.cz})
)

circuit.measure_all()

# Initialize a noiseless simulator.
sim0 = tq.Simulator()

# Initialize a simulator with depolarizing noise.

# Initialize a simulator with pauli noise and overrotation.
sim2 = (
tq.Simulator()
)

# Initialize a simulator with dephasing noise.
kraus_ops = [np.sqrt(0.99) * np.eye(2), np.sqrt(0.01) * np.diag([1, -1])]

# Run circuit on each of the simulators above and print the results.
sim0.run(circuit)
print(circuit.results)

sim1.run(circuit, overwrite=True)
print(circuit.results)

sim2.run(circuit, overwrite=True)
print(circuit.results)

sim3.run(circuit, overwrite=True)
print(circuit.results)


This produces the following output:

Results({'11': 50})
Results({'01': 1, '11': 48, '10': 1})
Results({'01': 2, '11': 47, '10': 1})
Results({'11': 50})


Note

Every time that a Circuit is run on a Simulator, the results are overwritten.