Simulator: Introduction

In this example, we go through the basic features of the True-Q™ simulator. We begin by importing True-Q™ and instantiating a circuit to play with.

import trueq as tq
import matplotlib.pyplot as plt
import numpy as np

circuit = tq.Circuit([{0: tq.Gate.x, 1: tq.Gate.y}, {(0, 1): tq.Gate.cz}])
circuit.measure_all()
True-Q formatting will not be loaded without trusting this notebook or rerunning the affected cells. Notebooks can be marked as trusted by clicking "File -> Trust Notebook".
Circuit
Key:
No key present in circuit.
   
 
(0): Gate.x
Name:
  • Gate.x
Aliases:
  • Gate.x
  • Gate.cliff1
Generators:
  • 'X': 180.0
Matrix:
  • 1.00 1.00
(1): Gate.y
Name:
  • Gate.y
Aliases:
  • Gate.y
  • Gate.cliff2
Generators:
  • 'Y': 180.0
Matrix:
  • -1.00j 1.00j
imm
(0, 1): Gate.cz
Name:
  • Gate.cz
Aliases:
  • Gate.cz
Likeness:
  • CNOT
Generators:
  • 'IZ': 90.0
  • 'ZI': 90.0
  • 'ZZ': -90.0
Matrix:
  • 1.00 1.00 1.00 -1.00
 
imm
(0): Meas()
Name:
  • Meas()
(1): Meas()
Name:
  • Meas()


Simulator Basics

When we instantiate a new simulator object, it is always noiseless.

sim = tq.Simulator()

We can use it to simulate the final state of the circuit, with all initial states prepared as \(|0\rangle\). Since the simulator is noiseless, the output state is a pure state; a noisy simulator will generally return a density matrix instead. If we want to force the output to be a density matrix, we can use sim0.state(circuit).upgrade().mat().

sim.state(circuit).mat()

Out:

array([0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j])

We can also use it to compute the overall action of the circuit. Since the simulator is currently noiseless, the output is a unitary matrix; a noisy simulator will return a superoperator (in the rowstacked basis).

tq.visualization.plot_mat(sim.operator(circuit).mat())
intro

Finally, we can use a simulator to populate the results of the circuit. Currently, the simulator has no results:

circuit.results

Out:

Results({})

But after calling the run() method, it has results that are randomly sampled bitstrings from the final state of a state simulation. In this case all 100 shots end in the "11" state because the final state is a computational eigenstate.

sim.run(circuit, n_shots=100)
circuit.results

Out:

Results({'11': 100})

Adding Noise Sources

We construct a noisy simulator by appending noise sources to a noiseless simulator.

Simulation is cycle-based. Each noise source is called to add noise to the quantum state (or to the superoperator if operator() is called) for each cycle in a circuit. The order that noise sources are applied is dictated by the order in which they were added to the simulator.

# Add an overrotation noise, which causes single qubit gates to be simulated as U^1.02
sim.add_overrotation(0.02)

# Add a depolarizing noise source at a rate of 0.8% per acted-on qubit per cycle
sim.add_depolarizing(0.008)

# Note that noisy simulators can be constructed as one-liners
other_sim = tq.Simulator().add_overrotation(0.02).add_depolarizing(0.008)

Now when we ask for the final state, we get a density operator rather than a pure state:

tq.visualization.plot_mat(sim.state(circuit).mat())
intro

And after calling the run() method (overwriting the results from above), we end up with noisy results:

sim.run(circuit, n_shots=100, overwrite=True)
circuit.results

Out:

Results({'01': 1, '11': 99})

We can also specify infinite shots to get the expectation values of each bitstring.

sim.run(circuit, n_shots=np.inf, overwrite=True)
circuit.results

Out:

Results({'00': 7.99041612947897e-05, '01': 0.008859008596685874, '10': 0.008859008596685874, '11': 0.9822020786453306})

Restricting Noise to Certain Labels or Gates

Many of the noise sources have the options noisy_labels and noisy_gates which specify that only certain qubits or gates should recieve the noise source in question.

# initialize a simulator with different dephasing rates on the qubits
bitflip = lambda p: [np.sqrt(1 - p) * np.eye(2), np.sqrt(p) * np.fliplr(np.eye(2))]
sim0 = tq.Simulator()
sim0.add_kraus(bitflip(0.05), noisy_labels=0)
sim0.add_kraus(bitflip(0.09), noisy_labels=1)

# initialize a simulator that targets only a specific gate
sim1 = tq.Simulator().add_kraus(bitflip(0.15), noisy_gates=tq.Gate.x)

# initialize a simulator that targets only specific gates on specific labels
sim2 = tq.Simulator()
sim2.add_kraus(bitflip(0.1), noisy_gates=[tq.Gate.y, tq.Gate.s], noisy_labels={1, 2})

# plot the final states
plt.figure(figsize=(10, 3))
tq.visualization.plot_mat(sim0.state(circuit).mat(), ax=plt.subplot("131"))
tq.visualization.plot_mat(sim1.state(circuit).mat(), ax=plt.subplot("132"))
tq.visualization.plot_mat(sim2.state(circuit).mat(), ax=plt.subplot("133"))
intro

Out:

Warning: Passing non-integers as three-element position specification is deprecated since 3.3 and will be removed two minor releases later.
         (/home/user/jenkins/workspace/release trueq/docs/examples/simulation/intro.py:116)
Warning: Passing non-integers as three-element position specification is deprecated since 3.3 and will be removed two minor releases later.
         (/home/user/jenkins/workspace/release trueq/docs/examples/simulation/intro.py:117)
Warning: Passing non-integers as three-element position specification is deprecated since 3.3 and will be removed two minor releases later.
         (/home/user/jenkins/workspace/release trueq/docs/examples/simulation/intro.py:118)

Total running time of the script: ( 0 minutes 0.195 seconds)

Gallery generated by Sphinx-Gallery