Simulation¶
A (noisy) quantum simulator for 

Appends a noise source to this simulator that adds isotropic depolarizing noise to the simulation. 

Appends a noise source to this simulator that adds Kraus noise to the simulation. 

Appends a noise source to this simulator that performs gate over/underrotation. 

Appends measurement noise in the form of a positive operatorvalued measurement (POVM). 

Appends a state preparation description to this simulator that chooses which state to prepare whenever a 

Appends measurement classification error to this simulator, where errors are specified by confusion matrices. 

Appends a noise source to this simulator that adds \(T_1\) and \(T_2\) relaxation noise to the simulation. 

Appends a noise source to this simulator that introduces stochastic Pauli noise. 

Returns the unitary or superoperator that results from simulating the given circuit. 

Updates the 

Returns the quantum state that results from simulating the given circuit. 

Wraps a function that mutates quantum states or operators by given circuit cycles. 
Simulator¶

class
trueq.
Simulator
¶ A (noisy) quantum simulator for
Circuit
objects that can be used to compute the finalstate()
, the total effectiveoperator()
, or simplyrun()
them tosample()
shots.import trueq as tq # initialize a simulator with no noise present sim = tq.Simulator() # initialize a simulator with depolarizing noise at rate 1% sim = tq.Simulator().add_depolarizing(0.01) # initialize a simulator with depolarizing noise at rate 1%, and unitary # overration by 5% sim = tq.Simulator().add_depolarizing(0.01).add_overrotation(0.05) # make some circuits and populate their results with random shots circuits = tq.make_srb([0], [4, 60]) sim.run(circuits, n_shots=100) circuits[0].results
Results({'0': 97, '1': 3})

append_cycle_noise
(propagation_fn, noise_only=False, is_prep=False, is_meas=False, dim=None)¶ Appends a custom noise source to the simulator. A noise source is a function with arguments
(cycle, state, cache)
that takes aCycle
and uses it to modify a state, which is an instance ofTensor
.import trueq import numpy as np # create a circuit to test on circuit = tq.Circuit([{0: tq.Prep()}, {0: tq.Gate.x}, {0: tq.Meas()}]) # an ideal pure state simulator with no caching def fun(cycle, state, cache): for labels, gate in cycle.gates.items(): state.apply_matrix(labels, gate.mat) sim = trueq.Simulator() results = sim.append_cycle_noise(fun).sample(circuit, 100) print("Ideal example:", results) # an ideal simulator that prepares the +> state every time a Prep is found def fun(cycle, state, cache): for label in cycle.prep_labels: state.update({(label,): [1 / np.sqrt(2), 1 / np.sqrt(2)]}) sim = trueq.Simulator() results = sim.append_cycle_noise(fun, is_prep=True).sample(circuit, 100) print("Prep example: ", results) # simulator that applies a Kraus map iid to all qubits (note that # Simulator.add_kraus is builtin and does the same thing) kraus_ops = [np.sqrt(0.9)*np.eye(2), np.sqrt(0.1)*np.array([[0,1],[1,0]])] S = sum(np.kron(a, a.conj().T) for a in kraus_ops) def fun(cycle, state, cache): for labels, gate in cycle.gates.items(): for label in labels: state.apply_matrix((label,), S) sim = trueq.Simulator() # the function prop does not apply gates in a cycle, so we are careful to # set noise_only to True results = sim.append_cycle_noise(fun, noise_only=True).sample(circuit, 100) print("Kraus example:", results)
Ideal example: Results({'1': 100}) Prep example: Results({'0': 51, '1': 49}) Kraus example: Results({'0': 8, '1': 92})
 Parameters
propagation_fn (
function
) – A function with arguments(cycle, state, cache)
that takes aCycle
and uses it to modify a state (an instance ofTensor
).cache
is a dictionary that is unique to thisCyclePropagator
that can be used in any way to cache computations.noise_only (
bool
) –True
iff this propagator only injects noise into the state, ie., it does not attempt to actually implement the cycle.prep_noise (
bool
) – Whether this propagator is used as a state preparer. This propagator is applied before any other on each cycle.meas_noise (
bool
) – Whether this propagator is used to simulate measurement. This propagator is integrated with measurement sampling and is applied only at the last cycle.dim (
None
int
) – The Hilbert space dimension of each subsystem expected by this propagator. A value ofNone
indicates that this propagator can propagate cycles of any dimension.

state
(circuit)¶ Returns the quantum state that results from simulating the given circuit.
If this simulator contains only unitary errors and pure state preparations, a pure state will be simulated and returned, otherwise, a density matrix will be simulated and returned. Unless this simulator contains a special preparation (see e.g.
add_prep()
), every qubit in the circuit will be prepared with the state \(0\rangle\).import trueq as tq # make a circuit with two clock cycles circuit = tq.Circuit([{(0, ): tq.Gate.h}, {(0, 1): tq.Gate.cnot}]) # simulate the circuit to find the final pure state psi = tq.Simulator().state(circuit) print("Pure state: ", psi.mat()) # if we add depolarizing noise we get a density matrix rho = tq.Simulator().add_depolarizing(0.01).state(circuit) print("Density matrix: ", rho.mat()) # we can get outcome probabilities from the state print("Pure state probabilities: ", psi.probabilities()) print("Density matrix probabilities: ", rho.probabilities()) # and we can convert them to Result objects print("Pure state as results: ", psi.probabilities().to_results()) print("Density matrix as results: ", rho.probabilities().to_results())
Pure state: [0.70710678+0.j 0. +0.j 0. +0.j 0.70710678+0.j] Density matrix: [[0.495025 +0.j 0. +0.j 0. +0.j 0.4851495+0.j] [0. +0.j 0.004975 +0.j 0. +0.j 0. +0.j] [0. +0.j 0. +0.j 0.004975 +0.j 0. +0.j] [0.4851495+0.j 0. +0.j 0. +0.j 0.495025 +0.j]] Pure state probabilities: Tensor(<[(2,), ()] on labels [(0, 1)]>) Density matrix probabilities: Tensor(<[(2,), ()] on labels [(0, 1)]>) Pure state as results: Results({'00': 0.5000000000000001, '11': 0.5000000000000001}) Density matrix as results: Results({'00': 0.49502500000000016, '01': 0.004975000000000002, '10': 0.004975000000000002, '11': 0.49502500000000016})
 Parameters
circuit (
Circuit
) – A circuit to find the final state of. Return type

operator
(circuit, gates_only=True)¶ Returns the unitary or superoperator that results from simulating the given circuit.
If this simulator contains only unitary errors and pure state preparations, a unitary will be simulated and returned, otherwise, a superoperator will be simulated and returned.
import trueq as tq # make a circuit with two clock cycles circuit = tq.Circuit([{(0, ): tq.Gate.h}, {(0, 1): tq.Gate.cnot}]) # simulate the circuit to find the final unitary u = tq.Simulator().operator(circuit) tq.visualization.plot_mat(u.mat()) # if we add depolarizing noise we get a superoperator s = tq.Simulator().add_depolarizing(0.01).operator(circuit) print("Super operator shape: ", s.mat().shape)
Super operator shape: (16, 16)
 Parameters
 Return type

sample
(circuit, n_shots=50)¶ Samples ditstrings from the final state of the simulated circuit. In contrast to
run()
, this method does not update the results of the circuit.import trueq as tq # make a circuit with two clock cycles and a measurement round circuit = tq.Circuit([{(0, ): tq.Gate.h}, {(0, 1): tq.Gate.cnot}]) circuit.measure_all() # instantiate a simulator with depolarizing and overrotation noise sim = tq.Simulator().add_depolarizing(0.01).add_overrotation(0.04) # sample 100 shots from the final state of the circuit print(sim.sample(circuit, 100))
Results({'00': 59, '01': 1, '10': 1, '11': 39})
 Parameters
circuit (
Circuit
) – A single circuit.n_shots (
int
float("inf")
) – The number of shots to sample. The final state of the circuit is simulated once and shots are drawn from the resulting probability distribution. Or, if this value is infinitylike (e.g.float("inf")
ornumpy.inf
) then results are populated with the exact simulated probabilities.
 Return type

run
(circuits, n_shots=50, overwrite=None)¶ Updates the
results
attribute of each given circuit by simulating its final quantum state and sampling shots from it.import trueq as tq # make a circuit with two clock cycles and a measurement round circuit = tq.Circuit([{(0, ): tq.Gate.h}, {(0, 1): tq.Gate.cnot}]) circuit.measure_all() # initialize a simulator with no noise sim = tq.Simulator() # run the circuit on the simulator 100 times to populate the results sim.run(circuit, n_shots=100) print(circuit.results) # instantiate a simulator with depolarizing and overrotation noise sim = tq.Simulator().add_depolarizing(0.01).add_overrotation(0.04) # we can also use run to evaluate circuit collections generated by protocols # like CB, SRB, IRB, and XRB on a simulator: circuits = tq.make_srb([0], [5, 50, 100], 30) sim.run(circuits, n_shots=100) circuits.plot.raw()
Results({'00': 47, '11': 53})
 Parameters
circuits (
Circuit
Iterable
) – A single circuit or an iterable of circuits.n_shots (
int
float("inf")
) – The number of shots to sample. The final state of the circuit is simulated once and shots are drawn from the resulting probability distribution. Or, if this value is infinitylike (e.g.float("inf")
ornumpy.inf
) then results are populated with the exact simulated probabilities.overwrite (
bool
None
) – IfFalse
, a circuit that already has results will have new simulation results added to the old results. IfTrue
orNone
, old results will be erased and replaced with new results, though a warning will be raised in the latter case ofNone
(default).

add_depolarizing
(p, d=2, noisy_labels=None, noisy_gates=None)¶ Appends a noise source to this simulator that adds isotropic depolarizing noise to the simulation. By default, noise is added every time a gate is encountered, however, by specifying
noisy_labels
and/ornoisy_gates
, the user can design a noise profile which is gate and/or qubitdependent. The noise map for a given gate is defined by\[\mathcal{D}(\rho) = (1p) \rho + p \text{Tr}(\rho) \mathcal{I} / d\]where \(d=2\) for qubits. If, for example, a twoqubit gate is encountered, the tensor product of two onequbit depolarizing channels will be simulated. By default, depolarizing noise is applied to every system being acted on by a gate in a given cycle. For example, if qubits (0,1) get a CNOT, and qubit 3 gets an X in a given cycle, then depolarizing noise is applied to only these three qubits, even if the preceeding cycle acted on qubit 2.
Note that this is noise only—it does not try to implement the cycle in question, but only adds noise to it.
import trueq circuit = tq.Circuit([{(0,1): tq.Gate.cx, 2: tq.Gate.h}]) # make a simulator with depolarizing noise with p=0.02 acting at every # location where a gate is applied sim = tq.Simulator().add_depolarizing(0.02) print(sim.sample(circuit, 1000)) # make a simulator where depolarizing noise acts only on locations where # gates act on qubit 5 sim = tq.Simulator().add_depolarizing(0.04, noisy_labels = {5}) print(sim.sample(circuit, 1000))
Results({'000': 484, '001': 495, '010': 2, '011': 5, '100': 8, '101': 5, '110': 1}) Results({'000': 515, '001': 485})
 Parameters
p (
float
) – The depolarizing parameter, as defined in the equation above.d (
int
) – The subsystem dimension, 2 for qubits.noisy_labels (
int
Iterable
) – An integer or iterable of integers specifying which qubit labels are affected by this noise process. All of a gate’s labels must be a subset of the noisy labels in order for it to be impacted by the noise. By default,noisy_labels
isNone
which causes all qubits to undergo this noise process.noisy_gates (
Gate
Iterable
) – A gate or iterable of gates which are affected by this noise process. By default,noisy_gates
isNone
and all gates will be acted upon by this noise process.
 Return type
 Returns
This simulator instance so that
add_*()
calls can be chained.

add_kraus
(kraus_ops, noisy_labels=None, noisy_gates=None)¶ Appends a noise source to this simulator that adds Kraus noise to the simulation. By default, the noise is added every time a gate is encountered. This noise is gateindependent (though it may depend on size of the gate, described below) and the noise map is defined by
\[\mathcal{K}(\rho) = \sum_{i} K_i \rho K_i^\dagger\]Note that this is noise only—it does not try to implement a given cycle in question, but only adds noise to it.
By default, Kraus noise is applied to every system being acted on by a gate in a given cycle. For example, if qubits (0,1) get a CNOT, and qubit 3 gets an X in a given cycle, then Kraus noise is applied to only these three qubits, even if the preceeding cycle acted on qubit 2.
Optionally, every gate size (determined by the number of labels it acts on) can specify its own set of Kraus operators. If an applicable gate is encountered whose size is not present in the collection of Kraus operators, then the single system (n_sys=1) Kraus operators are applied to every system that the gate acts on.
import trueq as tq import numpy as np circuit = tq.Circuit([{(0,1): tq.Gate.cx, 2: tq.Gate.h}]) # define qubit dephasing kraus operators kraus_ops = [np.sqrt(0.99)*np.eye(2), np.sqrt(0.01)*np.diag([1,1])] # initialize a simulator in which qubit dephasing noise acts on every # location where gates are applied sim = tq.Simulator().add_kraus(kraus_ops) print(sim.sample(circuit, 1000)) # qubit dephasing for single qubit gates, but some other kraus channel for # twoqubit gates kraus_ops = { 1: [np.sqrt(0.999) * np.eye(2), np.sqrt(0.001) * np.diag([1, 1])], 2: [ np.sqrt(0.99) * np.eye(4), np.sqrt(0.01) * np.fliplr(np.diag([1, 1, 1, 1])) ], } # initialize a simulator with the noise defined above acting at # every location where a gate acts sim = tq.Simulator().add_kraus(kraus_ops) print(sim.sample(circuit, 1000)) # initialize a simulator which applies the above noise when a gate acts # on qubit 0 sim = tq.Simulator().add_kraus(kraus_ops, noisy_labels={2}) print(sim.sample(circuit, 1000)) # initialize a simulator which applies the above noise when an X gate acts # on qubit 2 or 3 sim=tq.Simulator() sim.add_kraus(kraus_ops, noisy_labels={2, 3}, noisy_gates={tq.Gate.x}) print(sim.sample(circuit, 1000))
Results({'000': 485, '001': 515}) Results({'000': 517, '001': 480, '110': 1, '111': 2}) Results({'000': 484, '001': 516}) Results({'000': 513, '001': 487})
 Parameters
kraus_ops – Either a listlike of square matrices, each having the dimension of a single system; or, a dictionary whose keys are
n_sys
and whose values are listlikes of square matrices, specifying the Kraus operators for gates with that value ofn_sys
.noisy_labels (
int
Iterable
) – An integer or iterable of integers specifying which qubit labels are affected by this noise process. All of a gate’s labels must be a subset of the noisy labels in order for it to be impacted by the noise. By default,noisy_labels
isNone
which causes all qubits to undergo this noise process.noisy_gates (
Gate
Iterable
) – A gate or iterable of gates which are affected by this noise process. By default,noisy_gates
isNone
and all gates will be acted upon by this noise process.
 Return type
 Returns
This simulator instance so that
add_*()
calls can be chained.

add_overrotation
(single_sys=0, multi_sys=0, noisy_labels=None, noisy_gates=None)¶ Appends a noise source to this simulator that performs gate over/underrotation. This is done by propagating by the matrix power \(U_{err}=U^{(1+\epsilon)}\) instead of the ideal gate \(U\).
import trueq as tq circuit = tq.Circuit([{(0,1): tq.Gate.cx, 2: tq.Gate.h}]) # initialize a simulator in which every single qubit gate is overrotated by # epsilon=0.01 sim = tq.Simulator().add_overrotation(single_sys=0.01) print(sim.sample(circuit, 1000)) # initialize a simulator in which every single qubit gate is overrotated by # epsilon=0.02 and every multiqubit gate by epsilon=0.04 sim = tq.Simulator().add_overrotation(single_sys=0.02, multi_sys=0.04) print(sim.sample(circuit, 1000)) # initialize a simulator in which overrotations are applied whenever a gate # acts on any qubits in {0, 2, 3} sim = tq.Simulator() sim.add_overrotation(single_sys=0.02, multi_sys=0.01, noisy_labels={0, 2, 3}) print(sim.sample(circuit, 1000))
Results({'000': 521, '001': 479}) Results({'000': 496, '001': 504}) Results({'000': 492, '001': 508})
 Parameters
single_sys (
float
) – The amount of over/underrotation to apply to gates that act on a single subsystem (ie. singlequbit). A value of0
corresponds to no overrotation, a negative value corresponds to underrotation, and a positive value corresponds to overrotatian.multi_sys (
float
) – The amount of over/underrotation to apply to gates that act on a multiple subsystems (ie. multiqubit). A value of0
corresponds to no overrotation, a negative value corresponds to underrotation, and a positive value corresponds to overrotatian.noisy_labels (
int
Iterable
) – Which qubit labels are affected by this noise process. All of a gate’s labels must be a subset of the noisy labels in order for it to be impacted by the noise. By default,noisy_labels
isNone
which causes all qubits to undergo this noise process.noisy_gates (
Gate
Iterable
) – A gate or iterable of gates which are affected by this noise process. By default,noisy_gates
isNone
and all gates will be acted upon by this noise process.
 Return type
 Returns
This simulator instance so that
add_*()
calls can be chained.

add_povm
(povm)¶ Appends measurement noise in the form of a positive operatorvalued measurement (POVM). This is entered as a
Tensor
with shape((k,), (d, d))
wherek
is the number of outcomes per qubit, andd
is the Hilbert state dimension, which must match the simulation. A sum over thek
axis must produce the identity matrix for a probability preserving POVM.import trueq as tq import numpy as np # Define a set of ideal POVM operators ideal = np.array([[[1, 0], [0, 0]], [[0, 0], [0, 1]]]) # Define a unitary rotation about X by 5 degrees U = tq.Gate.from_generators("X", 5).mat # Define noisy POVM operators by rotating the ideal POVM operators by U twisted = [U @ x @ U.conj().T for x in ideal] # Specify how the operations defined above will impact the simulation # In this instance, the ideal POVMs will act on every qubit, except for # qubit 0, which will have the twisted POVM (rotation about X) povm = tq.math.Tensor( 2, (2, 2), # Number of POVMs, (dimensions of qubit subsystems) spawn=ideal, # Default measurement operation value={(0,): twisted}, # Measurement operation on qubit 0 dtype=np.complex128 # Tensor is real by default; this specifies complex ) # Initialize a simulator with the above POVM specifications. sim = tq.Simulator().add_povm(povm) # Initialize a circuit circuit = tq.Circuit([{(0, ): tq.Gate.h}, {(0, 1): tq.Gate.cnot}]) circuit.measure_all() # Run the circuit on the simulator sim.sample(circuit, 100)
Results({'00': 48, '11': 52})

add_prep
(state)¶ Appends a state preparation description to this simulator that chooses which state to prepare whenever a
Prep
operator is encountered in a circuit.A state to add can be specified in any of the following formats:
A number between 0 and 1, representing a bitflip error probability of the ideal preparation \(0\rangle\)
A length\(d\) vector, representing a pure state
A \(d\times d\) matrix, representing a density matrix
If only one of the above is provided, it is used every time a
Prep
object is encountered. One can also specify that different subsystems get different errors by providing a dictionary mapping whose values are combination of the above formats; see the examples below.import trueq as tq circuit = tq.Circuit([{i: tq.Prep() for i in range(3)}]) # add 5% bitflip error to any qubit sim = tq.Simulator().add_prep(0.05) print(sim.sample(circuit, float("inf"))) # add 2% bitflip error by default, but 5% for qubits 1 and 2 sim = tq.Simulator().add_prep({None: 0.02, 2: 0.05, 1: 0.05}) print(sim.sample(circuit, float("inf"))) # 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]]}) print(sim.sample(circuit, float("inf")))
Results({'000': 0.8573749999999999, '001': 0.045125, '010': 0.045125, '011': 0.0023750000000000004, '100': 0.045125, '101': 0.0023750000000000004, '110': 0.0023750000000000004, '111': 0.00012500000000000003}) Results({'000': 0.8844499999999998, '001': 0.04655, '010': 0.04655, '011': 0.0024500000000000004, '100': 0.01805, '101': 0.00095, '110': 0.00095, '111': 5e05}) Results({'000': 0.78408, '001': 0.00792, '010': 0.19602, '011': 0.00198, '100': 0.00792, '101': 8e05, '110': 0.00198, '111': 2e05})
For state simulation, if a
trueq.Prep
operator is encountered on a subsystem that has previously been initialized, then the register is tracedout and the state is replaced with a new one. For operator simulation,trueq.Prep
objects are taken as the partialtracewithreplacement channel \(\rho\mapsto\mathrm{Tr}_\text{i}(\rho)\otimes\rho'\) where \(i\) is the subsystem label being prepared, \(\rho\) is the old state on all subsystems, and \(\rho'\) is the new state being prepared on \(i\). Although, note thatoperator()
skips preparation objects by default. Parameters
state – A description of quantum states to initialize with.
 Return type
 Returns
This simulator instance so that
add_*()
calls can be chained.

add_readout_error
(default_error=None, errors=None)¶ Appends measurement classification error to this simulator, where errors are specified by confusion matrices. For qubits, errors can instead be specified by a single misclassification probability (symmetric readout error), or a pair of readout errors (asymmetric readout error, where the probability of misclassifying \(0\rangle\) is different than \(1\rangle\)).
Errors can differ on individual qubits by singling out their labels in the second argument, see the example below. Similarly, correlated errors can be added by specifying larger confusion matrices and the qubits they act on.
import trueq as tq # make a test circuit that measures 4 qubits circuit = tq.Circuit([{range(4): tq.Meas()}]) # all qubits get 1% readout error sim = tq.Simulator().add_readout_error(0.01) print(sim.sample(circuit, 200)) # all qubits get 1% readout error, except qubit 1 gets 5% readout error sim = tq.Simulator().add_readout_error(0.01, {1: 0.05}) print(sim.sample(circuit, 200)) # all qubits get 1% readout error, except qubit 1 get 5% readout error, # and qubit 3 get asymmetric readout error of 1% on 0> and 7% on 1> sim = tq.Simulator().add_readout_error(0.01, {1: 0.05, 3: [0.01,0.07]}) print(sim.sample(circuit, 200)) # we specify correlated measurement errors on qubits (3, 2) with a 4x4 # confusion matrix (row/column order is 00, 01, 10, 11). all other qubits get # 1% symmetric classification error. confusion = [ [0.81, 0.72, 0.72, 0.64], [0.09, 0.18, 0.08, 0.16], [0.06, 0.08, 0.18, 0.16], [0.04, 0.02, 0.02, 0.04] ] sim = tq.Simulator().add_readout_error(0.01, {(3,2):confusion}) print(sim.sample(circuit, 200)) # here we add asymmetric qutrit classification error to all three levels, where # 0> is reported as a '0' 99% of the time, 1> is reported as a '1' 92% of the # time, and 2> is reported as a '2' 80% of the time. offdiagonals are the # probabilities of misclassification into specific outcomes corresponding to the # row they are located in. we add this error specifically to qutrit 2, the rest # of the qutrits have ideal measurements. confusion = [[0.99, 0.03, 0.02], [0.005, 0.92, 0.18], [0.005, 0.05, 0.8]] sim = tq.Simulator().add_readout_error(errors={2: confusion}) print(sim.sample(circuit, 200)) # classify qutrit measurements into binary outcomes. this situation arises, for # example, in superconducting qubits when there is leakage into the third level # of the anharmonic oscillator, but readout always reports a '0' or a '1'. we # use a rectangular confusion matrix where 2> is classified as a '0' 70% of the # time, and as a '1' 30% of the time. confusion = [[0.99, 0.08, 0.7], [0.01, 0.92, 0.3]] sim = tq.Simulator().add_readout_error(confusion) print(sim.sample(circuit, 200))
Results({'0000': 195, '0001': 1, '0100': 3, '1000': 1}) Results({'0000': 188, '0001': 3, '0010': 2, '0100': 6, '1000': 1}) Results({'0000': 186, '0001': 1, '0100': 11, '1000': 2}) Results({'0000': 159, '0010': 15, '0001': 14, '0011': 9, '0100': 2, '0101': 1}) Results({'0000': 200}, dim=3) Results({'0000': 195, '0001': 2, '0010': 1, '0100': 1, '1000': 1})
 Parameters
default_error (
numpy.ndarray
like float
float
) – Default readout error specificed as a confusion matrix to be applied to all qubits that don’t receive special errors inerrors
. For qubits, the confusion matrix can be replaced by a single number or pair of numbers for symmetric and asymmetric classification error, respectively. The default no error.errors (
dict
) – A dictionary mapping tuples of qubit labels to confusion matrices. For qubits, confusion matrices can be replaced by a single number or pair of numbers for symmetric and asymmetric classification error, respectively.
 Return type
 Returns
This simulator instance so that
add_*()
calls can be chained.

add_relaxation
(t1, t2, t_single, t_multi, excited_pop=0)¶ Appends a noise source to this simulator that adds \(T_1\) and \(T_2\) relaxation noise to the simulation. Specifically, for every cycle in a circuit, this noise source adds error to every qubit in the state defined by the Choi matrix:
\[\begin{split}C = \begin{pmatrix} 1p(1e^{t/T_1}) & 0 & 0 & e^{t/T_2} \\ 0 & (1p)e^{t/T_1} & 0 & 0 \\ 0 & 0 & pe^{t/T_1} & 0 \\ e^{t/T_2} & 0 & 0 & 1(1p)e^{t/T_1} \end{pmatrix}\end{split}\]where \(t\) is the time of the longest gate in the cycle (i.e. either
t_single
ort_multi
for an entire cycle).import trueq circuit = tq.Circuit([{(0,1): tq.Gate.cx, 2: tq.Gate.h}]) # Make a simulator with relaxation noise with t1=100e6, t2=50e6, singlequbit # gate time 25e9, two qubit gate time 100e9 and an excited equilibrium # population 0.01. sim = tq.Simulator().add_relaxation(100e6, 50e6, 25e9, 100e9, 0.01) print(sim.sample(circuit, 1000)) # Make a simulator with relaxation noise with t1=10us, singlequbit gate time # 25ns and two qubit gate time 100ns. t2=5us on all qubits except qubit 0 # which has t2=2.5us. t2 = {None: 5e6, 0: 2.5e6} sim = tq.Simulator().add_relaxation(10e6, t2, 25e9, 100e9) # plot the final state tq.visualization.plot_mat(sim.state(circuit).mat())
Results({'000': 488, '001': 512})
 Parameters
t1 (
float
dict
) – The \(T_1\) time, i.e. the characteristic time of relaxation. These can vary from qubit to qubit, see the example above.t2 (
float
dict
) – The \(T_2\) time, i.e. the characteristic time of dephasing. These can vary from qubit to qubit, see the example above.t_single (
float
) – The time for a singlequbit gate.t_multi (
float
) – The time for a multiqubit gate.excited_pop (
float
dict
) – The excited state population at equilibrium.
 Return type
 Returns
This simulator instance so that
add_*()
calls can be chained.

add_stochastic_pauli
(px=0, py=0, pz=0, noisy_labels=None, noisy_gates=None)¶ Appends a noise source to this simulator that introduces stochastic Pauli noise. This is done by applying the following singlequbit Kraus operators to every qubit that is explicitly acted upon by a gate in a given cycle:
\[\begin{split}K_1 &= \sqrt{1p_xp_yp_z} \mathcal{I} \\ K_2 &= \sqrt{p_x} \sigma_x \\ K_3 &= \sqrt{p_y} \sigma_y \\ K_4 &= \sqrt{p_z} \sigma_z\end{split}\]Note that this is noise only—it does not try to implement the cycle in question, but only adds noise to it.
By default, this noise is applied to every system being acted on by a gate in a given cycle. For example, if qubits (0,1) get a CNOT, and qubit 3 gets an X in a given cycle, then stochastic Pauli noise is applied to only these three qubits, even if the preceeding cycle acted on qubit 2. However,
noisy_labels
andnoisy_gates
allow users to specify which gates and/or qubits are impacted by the noise. For example, ifnoisy_labels={0, 1}
andnoisy_gates={tq.Gate.x, tq.Gate.cz}
then noise will only be applied in locations where an X gate acts on qubit 0 or 1 and where a CZ gate acts on qubits 0 and 1.import trueq as tq circuit = tq.Circuit([{(0,1): tq.Gate.cx, 2: tq.Gate.x, 3: tq.Gate.h}]) # initialize a simulator in which every location where a gate acts undergoes # Pauli noise with px=0.01 and py=0.04 sim = tq.Simulator().add_stochastic_pauli(px=0.01, py=0.04) print(sim.sample(circuit, 1000)) # initialize a simulator where every X gate undergoes Pauli noise with # py=pz=0 and px=0.04 sim = tq.Simulator().add_stochastic_pauli(px=0.04, noisy_gates=tq.Gate.x) print(sim.sample(circuit, 1000))
Results({'0000': 17, '0001': 25, '0010': 446, '0011': 422, '0100': 1, '0101': 2, '0110': 21, '0111': 17, '1001': 2, '1010': 24, '1011': 22, '1111': 1}) Results({'0000': 17, '0001': 17, '0010': 494, '0011': 472})
 Parameters
px (
float
) – The probability of an X error.py (
float
) – The probability of a Y error.pz (
float
) – The probability of a Z error.noisy_labels (
int
Iterable
) – An integer or iterable of integers specifying which qubit labels are affected by this noise process. All of a gate’s labels must be a subset of the noisy labels in order for it to be impacted by the noise. By default,noisy_labels
isNone
which causes all qubits to undergo this noise process.noisy_gates (
Gate
Iterable
) – A gate or iterable of gates which are affected by this noise process. By default,noisy_gates
isNone
and all gates will be acted upon by this noise process.
 Return type
 Returns
This simulator instance so that
add_*()
calls can be chained.

CyclePropagator¶

class
trueq.simulation.simulator.
CyclePropagator
(propagation_fn, noise_only=False, dim=None)¶ Wraps a function that mutates quantum states or operators by given circuit cycles. This is a helper class for
Simulator
.from trueq.simulation.simulator import CyclePropagator # an ideal pure state simulator with no caching def prop(cycle, state, cache): for labels, gate in cycle: state.apply_matrix(labels, gate.mat) cycle_prop = CyclePropagator(prop)
 Parameters
propagation_fn (
function
) – A function with arguments(cycle, state, cache)
that takes aCycle
and uses it to modify a state (an instance ofTensor
).cache
is a dictionary that is unique to thisCyclePropagator
that can be used in any way to cache computations.noise_only (
bool
) –True
iff this propagator only injects noise into the state, ie., it does not attempt to actually implement the cycle. See also the documentation ofnoiseonly
.dim (
None
int
) – The Hilbert space dimension of each subsystem expected by this propagator. A value ofNone
indicates that this propagator can propagate cycles of any dimension.

property
dim
¶ The Hilbert space dimension of each subsystem expected by this propagator.
 Type
int

property
noise_only
¶ Whether this propagator only injects noise, as opposed to making an attempt to implement operations in a given cycle.
An example of a noiseonly propagator is one that multiplies the state or superoperator by a depolarizing channel everytime a gate is encountered; it makes no attempt to actually simulate the gate.
An example of nonnoisyonly propagator is an overrotation propagator that simulates the occurence of a gate \(G\) with \(G^{1+\epsilon}\); if \(\epsilon\) is small, it does implement a gate close to \(G\).
 Type
bool