Simulation

trueq.Simulator

A (noisy) quantum simulator for Circuit objects that can be used to compute the final state(), the total effective operator(), or simply run() them to sample() shots.

trueq.Simulator.add_depolarizing

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

trueq.Simulator.add_kraus

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

trueq.Simulator.add_overrotation

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

trueq.Simulator.add_povm

Appends measurement noise in the form of a positive operator-valued measurement (POVM).

trueq.Simulator.add_prep

Appends a state preparation description to this simulator that chooses which state to prepare whenever a operator is encountered in a circuit.

trueq.Simulator.add_readout_error

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

trueq.Simulator.add_relaxation

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

trueq.Simulator.add_stochastic_pauli

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

trueq.Simulator.operator

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

trueq.Simulator.run

Updates the results attribute of each given circuit by simulating its final quantum state and sampling shots from it.

trueq.Simulator.state

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

trueq.Simulator.dressed_noise

Computes the dressed noise of the given cycle for every list of labels in labels_list.

trueq.Simulator.predict_cb

Predicts the estimates that would be generated by simulating make_cb() with this simulator.

trueq.Simulator.predict_irb

Predicts the estimates that would be generated by simulating make_irb() with this simulator.

trueq.Simulator.predict_knr

Predicts the estimates that would be generated by simulating make_knr() with this simulator.

trueq.Simulator.predict_srb

Predicts the estimates that would be generated by simulating make_srb() with this simulator.

trueq.simulation.simulator.CircuitPropagator

A helper class to Simulator that abstracts the logic of iterating through the cycles of a circuit and applying noise sources to whatever kind of state is provided.

trueq.simulation.match.Match

A class whose purpose is to filter the operations presented to a given NoiseSource in a Simulator instance.

trueq.simulation.noise_source.NoiseSource

Parent class for all built in noise sources which can be added to the simulator.

trueq.simulation.match.OpWrapper

Wraps labels and an Operation into one small object.

trueq.simulation.match.CycleWrapper

Wraps a Cycle into a list of OpWrappers, partitioning the cycle ops into four groups, Block which contains all blocks, Gate which contains all gates, Meas which contains all measurements, and Prep which contains all state preparations in the cycle being propagated by the simulator.

Simulator

class trueq.Simulator

A (noisy) quantum simulator for Circuit objects that can be used to compute the final state(), the total effective operator(), or simply run() them to sample() 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': 3, '1': 97})
add_depolarizing(p, d=2, match=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 match, the user can design a noise profile which is gate- and/or qubit-dependent. See Match for more details. The noise map for a given gate is defined by

\[\mathcal{D}(\rho) = (1-p) \rho + p \text{Tr}(\rho) \mathcal{I} / d\]

where \(d=2\) for qubits. If, for example, a two-qubit gate is encountered, the tensor product of two one-qubit 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 contained a gate which acted on qubit 2. To ensure a depolarizing channel acts on all qubits during each cycle, include identity gates explicitly in the cycle definition.

Note that this is noise only—it does not try to implement the cycle in question, but only adds noise to it.

import trueq as tq
import trueq.simulation as tqs

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, match=tqs.LabelMatch(5))
print(sim.sample(circuit, 1000))
Results({'000': 487, '001': 496, '010': 1, '011': 8, '110': 3, '111': 5})
Results({'000': 503, '001': 497})
Parameters
  • p (float) – The depolarizing parameter, as defined in the equation above.

  • d (int) – The subsystem dimension, 2 for qubits.

  • match (Match | None) – A match object which determines the conditions required for a noise source to attempt to apply itself to the state during simulation.

Returns

This simulator instance so that add_*() calls can be chained.

Return type

Simulator

Raises

NotImplementedError – If not called for qubits.

add_kraus(kraus_ops, match=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 gate-independent (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 had a gate which acted on qubit 2. To ensure that the Kraus channel acts on all qubits during each cycle, include identity gates explicitly in the cycle definition.

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
# two-qubit 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, match=tqs.LabelMatch(0))
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()
noise_match = tqs.LabelMatch((2,3)) & tqs.GateMatch(tq.Gate.x)
sim.add_kraus(kraus_ops, match=noise_match)
print(sim.sample(circuit, 1000))
Results({'000': 483, '001': 517})
Results({'000': 495, '001': 496, '101': 9})
Results({'000': 506, '001': 494})
Results({'000': 491, '001': 509})
Parameters
  • kraus_ops (list | dict) – Either a list-like of square matrices, each having the dimension of a single system; or, a dictionary whose keys are n_sys and whose values are list-likes of square matrices, specifying the Kraus operators for gates with that value of n_sys.

  • match (Match | None) – A match object which determines the conditions required for a noise source to attempt to apply itself to the state during simulation.

Returns

This simulator instance so that add_*() calls can be chained.

Return type

Simulator

Raises

ValueError – If kraus_ops has the wrong dimension or is not a list of square matrices.

add_overrotation(single_sys=0, multi_sys=0, match=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\). Overrotation noise does attempt to simulate the gates within a circuit, it is not just noise only.

import trueq as tq
import trueq.simulation as tqs

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 multi-qubit 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}
sim = tq.Simulator()
noisy_labels = tqs.LabelMatch((0,2))
sim.add_overrotation(single_sys=0.02, multi_sys=0.01, match=noisy_labels)
print(sim.sample(circuit, 1000))
Results({'000': 500, '001': 500})
Results({'000': 505, '001': 495})
Results({'000': 472, '001': 528})
Parameters
  • single_sys (float) – The amount of over/underrotation to apply to gates that act on a single subsystem (ie. single-qubit). A value of 0 corresponds to no overrotation, a negative value corresponds to underrotation, and a positive value corresponds to overrotation.

  • multi_sys (float) – The amount of over/underrotation to apply to gates that act on a multiple subsystems (ie. multi-qubit). A value of 0 corresponds to no overrotation, a negative value corresponds to underrotation, and a positive value corresponds to overrotation.

  • match (Match | None) – A match object which determines the conditions required for a noise source to attempt to apply itself to the state during simulation.

Returns

This simulator instance so that add_*() calls can be chained.

Return type

Simulator

add_povm(povm, match=None)

Appends measurement noise in the form of a positive operator-valued measurement (POVM). This is entered as a Tensor with shape ((k,), (d, d)) where k is the number of outcomes per qubit, and d is the Hilbert state dimension, which must match the simulation. A sum over the k 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),             # num 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
)

# 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})
Parameters
  • povm (Tensor) – A tensor describing a POVM.

  • match (Match | None) – A match object which determines the conditions required for a noise source to attempt to apply itself to the state during simulation.

Returns

This simulator instance so that add_*() calls can be chained.

Return type

Simulator

add_prep(state, match=None)

Appends a state preparation description to this simulator that chooses which state to prepare whenever a 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': 5e-05})
Results({'000': 0.78408, '001': 0.00792, '010': 0.19602, '011': 0.00198, '100': 0.00792, '101': 8e-05, '110': 0.00198, '111': 2e-05})

For state simulation, if a Prep operator is encountered on a subsystem that has previously been initialized, then the register is traced-out and the state is replaced with a new one. For operator simulation, Prep objects are taken as the partial-trace-with-replacement 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 that operator() skips preparation objects by default.

Parameters
  • state (float | numpy.ndarray-like | dict) – A description of quantum states to initialize with.

  • match (Match | None) – A match object which determines the conditions required for a noise source to attempt to apply itself to the state during simulation.

Returns

This simulator instance so that add_*() calls can be chained.

Return type

Simulator

Raises

ValueError – If inconsistent dimensions are provided.

add_readout_error(default_error=None, errors=None, match=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.
# off-diagonals 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': 196, '0010': 1, '1000': 3})
Results({'0000': 179, '0010': 4, '0100': 14, '1000': 2, '1010': 1})
Results({'0000': 181, '0001': 2, '0010': 4, '0100': 12, '1000': 1})
Results({'0000': 160, '0010': 18, '0001': 15, '0011': 7})
Results({'0000': 198, '0010': 2}, dim=3)
Results({'0000': 195, '0001': 1, '0010': 3, '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 in errors. 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.

  • match (Match | None) – A match object which determines the conditions required for a noise source to attempt to apply itself to the state during simulation.

Returns

This simulator instance so that add_*() calls can be chained.

Return type

Simulator

Raises

ValueError – For any inconsistencies in the input arguments.

add_relaxation(t1, t2, t_single, t_multi, excited_pop=0, match=None)

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} 1-p(1-e^{-t/T_1}) & 0 & 0 & e^{-t/T_2} \\ 0 & (1-p)e^{-t/T_1} & 0 & 0 \\ 0 & 0 & pe^{-t/T_1} & 0 \\ e^{-t/T_2} & 0 & 0 & 1-(1-p)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 or t_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=100e-6, t2=50e-6,
# single-qubit gate time 25e-9, two qubit gate time 100e-9 and an excited
# equilibrium population 0.01.
sim = tq.Simulator().add_relaxation(100e-6, 50e-6, 25e-9, 100e-9, 0.01)
print(sim.sample(circuit, 1000))

# make a simulator with relaxation noise with t1=10us, single-qubit gate
# time 25ns and two qubit gate time 100ns. t2=5us on all qubits except qubit
# 0 which has t2=2.5us.
t2 = {None: 5e-6, 0: 2.5e-6}
sim = tq.Simulator().add_relaxation(10e-6, t2, 25e-9, 100e-9)
# plot the final state
tq.visualization.plot_mat(sim.state(circuit).mat())
Results({'000': 525, '001': 475})
../_images/simulation_7_1.png
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 it takes to perform a single-qubit gate.

  • t_multi (float) – The time it takes to perform a multi-qubit gate.

  • excited_pop (float | dict) – The excited state population at equilibrium.

  • match (Match | None) – A match object which determines the conditions required for a noise source to attempt to apply itself to the state during simulation.

Returns

This simulator instance so that add_*() calls can be chained.

Return type

Simulator

Raises

ValueError – If invalid \(T_1\) and \(T_2\) arguments are provided.

add_stochastic_pauli(px=0, py=0, pz=0, match=None)

Appends a noise source to this simulator that introduces stochastic Pauli noise. This is done by applying the following single-qubit Kraus operators to every qubit that is explicitly acted upon by a gate in a given cycle:

\[\begin{split}K_1 &= \sqrt{1-p_x-p_y-p_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. The match argument can be used to specify under which conditions the stochastic Pauli noise should be applied, see Match for more information.

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
noisy_gate = tqs.GateMatch(tq.Gate.x)
sim = tq.Simulator().add_stochastic_pauli(px=0.04, match = noisy_gate)
print(sim.sample(circuit, 1000))
Results({'0000': 19, '0001': 18, '0010': 404, '0011': 459, '0100': 1, '0110': 22, '0111': 25, '1010': 1, '1011': 4, '1100': 1, '1110': 18, '1111': 28})
Results({'0000': 18, '0001': 18, '0010': 443, '0011': 521})
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.

  • match (Match | None) – A match object which determines the conditions required for a noise source to attempt to apply itself to the state during simulation.

Returns

This simulator instance so that add_*() calls can be chained.

Return type

Simulator

Raises

ValueError – If the probabilities of X, Y, and Z errors sum to more than 1.

append_noise_source(noise_source)

Appends a noise source to the simulator. A noise source in an instance of some subclass of NoiseSource, which includes custom user subclasses.

The order in which noise sources are added is the order in which they will be applied (cycle by cycle) during simulation.

Parameters

noise_source (NoiseSource) – A source of noise to be added to the simulator.

Returns

This simulator instance.

Return type

Simulator

dressed_noise(cycle, labels_list, n_randomizations=None, twirl='P', compiler=None)

Computes the dressed noise of the given cycle for every list of labels in labels_list. The dressed noise superoperator is defined as

\[\mathcal{E} = \mathbb{E}[ \mathcal{T}^\dagger \mathcal{C}^\dagger \tilde{\mathcal{C}} \tilde{\mathcal{T}} ]\]

where \(\mathcal{C}\) is the cycle of interest, \(\tilde{\mathcal{C}}\) its noisy simulation, \(\mathcal{T}\) an element of the twirling group, \(\tilde{\mathcal{T}}\) its noisy simulation, and where the expectation is taken uniformly over the twirling group.

The expectation value is taken in one of two ways. If n_randomizations=None, twirling operations are assumed to be perfect so that the expectation value is a simple function of the diagonal elements of the superoperator \(\mathcal{C}^\dagger \tilde{\mathcal{C}}\) in the Pauli vectorization basis. Otherwise, the expectation is computed using n_randomizations Monte-Carlo samples from the twirling group, each of which is simulated with this simulator.

Parameters
  • cycle (Cycle) – The cycle to find the dressed noise of.

  • labels_list (Iterable) – A list of lists of labels. Simulations are performed once on all labels present, and marginalizations are performed on each of these lists afterwards.

  • n_randomizations (int | None) – The number of random twirls to average over. If None, the twirling group is assumed to have a perfect implementation.

  • twirl (Twirl | str) – The twirling group to use.

  • compiler (Compiler) – A compiler to run all cycles through before simulating them.

Returns

A list of Tensors, corresponding to the given labels_list. The tensors report Pauli probabilities of the Pauli Kraus map.

Return type

list

predict_cb(cycle, labels=None, n_randomizations=50, targeted_errors=None, twirl='P', compiler=None)

Predicts the estimates that would be generated by simulating make_cb() with this simulator.

import trueq as tq

sim = tq.Simulator().add_stochastic_pauli(px=0.01).add_overrotation(0.04)

sim.predict_cb({0: tq.Gate.id, (1,2): tq.Gate.cnot})
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".
CB
Cycle Benchmarking
Paulis

(0,) : Gate.id

(1, 2) : Gate.cx

Key:
  • cycle: Cycle((0,): Gate.id, (1, 2): Gate.cx)
  • labels: (0, 1, 2)
  • name:
  • protocol: CB
  • twirl: Paulis on [0, 1, 2]
${e}_{F}$
The probability of an error acting on the specified labels during a dressed cycle of interest.
6.7e-02 (0.0e+00)
0.06667918822754859, 0.0
${e}_{III}$
The probability of the subscripted error acting on the specified labels.
9.3e-01 (0.0e+00)
0.9333208117724514, 0.0
Parameters
  • labels (Iterable) – A list of which sets of system labels are to be twirled together in each circuit, e.g. [3, [1, 2], 4].

  • n_randomizations (int | None) – The number of random twirls to average over. If None, the twirling group is assumed to have a perfect implementation.

  • twirl (Twirl | str) – The twirling group to use.

  • compiler (Compiler) – A compiler to run all cycles through before simulating them.

Return type

EstimateCollection

Raises

ValueError – If targeted_errors are not all Pauli strings of the correct length.

predict_irb(cycle, n_randomizations=50, targeted_errors=None, twirl='C', compiler=None)

Predicts the estimates that would be generated by simulating make_irb() with this simulator.

import trueq as tq

sim = tq.Simulator().add_stochastic_pauli(px=0.01).add_overrotation(0, 0.1)

sim.predict_irb({0: tq.Gate.id, (1,2): tq.Gate.cnot})
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".
IRB
Interleaved Randomized Benchmarking
Cliffords

(0,) : Gate.id

(1, 2) : Gate.cx

Key:
  • cycle: Cycle((0,): Gate.id, (1, 2): Gate.cx)
  • labels: (0,)
  • name:
  • protocol: IRB
  • twirl: Cliffords on [0, (1, 2)]
Cliffords

(0,) : Gate.id

(1, 2) : Gate.cx

Key:
  • cycle: Cycle((0,): Gate.id, (1, 2): Gate.cx)
  • labels: (1, 2)
  • name:
  • protocol: IRB
  • twirl: Cliffords on [0, (1, 2)]
${e}_{F}$
The probability of an error acting on the targeted systems during a dressed gate of interest.
2.0e-02 (0.0e+00)
0.01985400000000026, 0.0
8.2e-02 (0.0e+00)
0.08243074620951019, 0.0
${p}$
Decay parameter of the exponential decay $Ap^m$.
9.8e-01 (0.0e+00)
0.9801459999999997, 0.0
9.2e-01 (0.0e+00)
0.9175692537904898, 0.0
Parameters
  • labels (Iterable) – A list of which sets of system labels are to be twirled together in each circuit, e.g. [3, [1, 2], 4].

  • n_randomizations (int | None) – The number of random twirls to average over. If None, the twirling group is assumed to have a perfect implementation.

  • twirl (Twirl | str) – The twirling group to use.

  • compiler (Compiler) – A compiler to run all cycles through before simulating them.

Return type

EstimateCollection

predict_knr(cycle, n_bodies=1, n_randomizations=50, twirl='P', compiler=None)

Predicts the estimates that would be generated by simulating make_knr() with this simulator.

import trueq as tq

sim = tq.Simulator().add_stochastic_pauli(px=0.01).add_overrotation(0, 0.1)

cycle = {0: tq.Gate.h, (1,2): tq.Gate.cnot}
prediction = sim.predict_knr(cycle, n_bodies=2)
prediction.plot.knr_heatmap()
../_images/simulation_11_0.png
Parameters
  • labels (dict | Cycle) – The cycle to make the prediction for.

  • n_randomizations (int | None) – The number of random twirls to average over. If None, the twirling group is assumed to have a perfect implementation.

  • twirl (Twirl | str) – The twirling group to use.

  • compiler (Compiler) – A compiler to run all cycles through before simulating them.

Return type

EstimateCollection

predict_srb(labels, n_randomizations=50, twirl='C', compiler=None)

Predicts the estimates that would be generated by simulating make_srb() with this simulator.

import trueq as tq

sim = tq.Simulator().add_stochastic_pauli(px=0.01).add_overrotation(0.04)

prediction = sim.predict_srb([0, [1,2]])
prediction += sim.predict_srb([0])
prediction += sim.predict_srb([[1, 2]])
prediction.plot.compare_rb()
../_images/simulation_12_0.png
Parameters
  • labels (Iterable) – A list of which sets of system labels are to be twirled together in each circuit, e.g. [3, [1, 2], 4].

  • n_randomizations (int | None) – The number of random twirls to average over. If None, the twirling group is assumed to have a perfect implementation.

  • twirl (Twirl | str) – The twirling group to use.

  • compiler (Compiler) – A compiler to run all cycles through before simulating them.

Return type

EstimateCollection

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.4975    +0.j 0.        +0.j 0.        +0.j 0.48759975+0.j]
 [0.        +0.j 0.0025    +0.j 0.00245025+0.j 0.        +0.j]
 [0.        +0.j 0.00245025+0.j 0.0025    +0.j 0.        +0.j]
 [0.48759975+0.j 0.        +0.j 0.        +0.j 0.4975    +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.4975000000000001, '01': 0.002500000000000001, '10': 0.002500000000000001, '11': 0.4975000000000001})
Parameters

circuit (Circuit) – A circuit to find the final state of.

Return type

StateTensor

Raises

NotImplementedError – If the circuit contains measurements before the final cycle.

operator(circuit)

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)
../_images/simulation_14_1.png

Note

By default, this method skips Prep and Meas operators.

Parameters

circuit (Circuit) – A circuit to find the final operator of.

Returns

The effective operator of the circuit after simulation.

Return type

OperatorTensor

Raises

NotImplementedError – If the last cycle in the circuit contains measurements on some qubits but not all.

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': 42, '11': 58})
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 infinity-like (e.g. float("inf") or numpy.inf) then results are populated with the exact simulated probabilities.

Return type

Results

run(circuits, n_shots=50, overwrite=None, max_workers=1)

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': 44, '11': 56})
../_images/simulation_16_1.png
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 infinity-like (e.g. float("inf") or numpy.inf) then results are populated with the exact simulated probabilities.

  • overwrite (bool | None) – If False, a circuit that already has results will have new simulation results added to the old results. If True or None, old results will be erased and replaced with new results, though a warning will be raised in the latter case of None (default).

  • max_workers (int | None) – The maximum number of workers to use when parallelizing this simulation over circuits. A value of None defaults to the number of processors on the machine. This feature is not available on Windows, which defaults to serial simulation.

CircuitPropagator

class trueq.simulation.simulator.CircuitPropagator(obj, noise_sources)

A helper class to Simulator that abstracts the logic of iterating through the cycles of a circuit and applying noise sources to whatever kind of state is provided.

During initialization of this class, the constructor checks if any of the NoiseSources added to the simulator have specified required dimensions via the noise source’s dimension, dim, and ensures they match the subsystem dimensions of the Circuit to be simulated. If there is a dimension mismatch, and error will be raised. An additional error will be raised if a measurement occurs before the last cycle within the circuit.

Each noise source will, in order, attempt to create a cache of information about the circuit. For more information, see make_circuit_cache().

Parameters
  • obj (Circuit | Cycle | list | dict) – A circuit (a list of cycles) or a cycle (a dictionary of operations) to be propagated. If neither, an error is raised.

  • noise_sources (list) – A list of noise sources.

Raises
  • ValueError – If obj isn’t recognized as a circuit or a cycle, and if a NoiseSource has a specified dimension which does not match the subsystem dimensions of obj.

  • NotImplementedError – If there are measurements before the last cycle of a circuit.

propagate(state)

Cycle by cycle, this function gives each noise source, in order, the opportunity to mutate the state, effectively simulating the Circuit.

Parameters

state (StateTensor | OperatorTensor) – The initial state undergoing propagation.

Returns

The final state at the end of simulation.

Return type

StateTensor | OperatorTensor

Raises

NotImplementedError – If a measurement is found in a cycle prior to the last cycle in the circuit.

Match

class trueq.simulation.match.Match(exclusive=False)

A class whose purpose is to filter the operations presented to a given NoiseSource in a Simulator instance. This is typically used by a noise source to specify, for example, which gate, label, and cycle combinations to apply noise to. This base class is a pass-through that does not do any filtering except the following which is common to all noise sources:

  • Any presented OpWrapper where no_more_noise is True is skipped.

The match also defines iter_* functions, which are called by NoiseSources, and return an iterator of pairs of Operations and labels (tuples). The noise source function called during propagation, apply(), does not call iter() directly, which returns an iterator of OpWrappers, but instead calls to the match via the iter_* wrapper functions, meaning that a NoiseSource does not (and should not) need to interact with a OpWrapper.

Two or more matches can be joined together with & (AndMatch) and | (OrMatch) that restrict operations to the intersection of matches, or the union of matches, respectively. This is seen in the following examples:

import trueq as tq
import trueq.simulation as tqs

# Create a match that matches only X gates.
noisy_x = tqs.GateMatch(tq.Gate.x)

# Create a match that matches only Y gates.
noisy_y = tqs.GateMatch(tq.Gate.y)

# Create a match that matches any operation acting on a subset of labels
# (2, 4).
noisy_labels = tqs.LabelMatch((2,4))

# Single label matches can send in the label as an integer instead of a tuple
# of length 1. This match matches all operations on qubit 0.
zero_match = tqs.LabelMatch(0)

# A match which prevents subsequent noise sources from seeing operations
# that are matched with this one.
no_more_match = tqs.Match(exclusive=True)

# Create a match that restricts to X gates and any gate on qubits 2 and 4
noise_restriction = tqs.OrMatch(noisy_x, noisy_labels)

# Equivalently, we can use ``|``
noise_restriction = noisy_x | noisy_labels

# Create a match that restricts to X gates only on qubits 2 and 4
noise_restriction = tqs.AndMatch(noisy_x, noisy_labels)

# Equivalently, we can use ``&``
noise_restriction = noisy_x & noisy_labels

# Create a match that restricts to Y gates on qubits 2 and 4, and any X gates
compound_match = (noisy_y & noisy_labels) | noisy_x

# The following produce equivalent match behavior:
equiv1 = noisy_x | noisy_y
equiv2 = tqs.GateMatch([tq.Gate.x, tq.Gate.y])
iter(op_type, cycle_wrapper, noise_only=True, update_flags=True)

Iterates through the operations in the cycle_wrapper, yielding every operation of type op_type that is matched by this instance and that has not yet been marked for no further simulation via Opwrapper.no_more_noise.

Parameters
  • op_type (Block | Gate | Meas | Prep) – The type of Operation to extract from the cycle_wrapper.

  • cycle_wrapper (CycleWrapper) – The cycle to match against.

  • noise_only (bool) – Whether to mark the OpWrapper as has_been_simulated after it has been yielded.

  • update_flags (bool) – Whether to mutate OpWrapper flags when an operation is yielded. Only composite matches such as AndMatch should consider setting this to False.

Returns

An iterator of OpWrappers with operations of type op_type

Return type

Iterable

iter_block(cycle_wrapper, noise_only=True)

Yields pairs (labels, block) in the given cycle_wrapper where block is a Block that is matched by this instance.

Parameters
  • cycle_wrapper (CycleWrapper) – The cycle to match against.

  • noise_only (bool) – Whether to mark the OpWrapper as has_been_simulated after it has been yielded.

Returns

An iterator of labels and operators.

Return type

Iterable

iter_gates(cycle_wrapper, noise_only=True)

Yields pairs (labels, gate) in the given cycle_wrapper where gate is a Gate that is matched by this instance.

Parameters
  • cycle_wrapper (CycleWrapper) – The cycle to match against.

  • noise_only (bool) – Whether to mark the OpWrapper as has_been_simulated after it has been yielded.

Returns

An iterator of labels and operators.

Return type

Iterable

iter_meas(cycle_wrapper, noise_only=True)

Yields pairs (labels, meas) in the given cycle_wrapper where meas is a Meas that is matched by this instance.

Parameters
  • cycle_wrapper (CycleWrapper) – The cycle to match against.

  • noise_only (bool) – Whether to mark the OpWrapper as has_been_simulated after it has been yielded.

Returns

An iterator of labels and operators.

Return type

Iterable

iter_prep(cycle_wrapper, noise_only=True)

Yields pairs (labels, prep) in the given cycle_wrapper where prep is a Prep that is matched by this instance.

Parameters
  • cycle_wrapper (CycleWrapper) – The cycle to match against.

  • noise_only (bool) – Whether to mark the OpWrapper as has_been_simulated after it has been yielded.

Returns

An iterator of labels and operators.

Return type

Iterable

class trueq.simulation.match.LabelMatch(labels, strict=True, exclusive=False)

Instances of this class match operations whose qubit labels correspond to a specified set of labels. Operations are matched as follows:

  • If strict is true, operations whose labels are a strict subset of this instance’s labels are yielded.

  • If strict is False, operations whose labels have some intersection with this instance’s labels are yielded.

  • Any presented OpWrapper where no_more_noise is True is skipped.

For example, a LabelMatch initialized with the label 0 will match to single qubit operations on label 0, regardless of the value of strict. If strict is False, it will also match to any multi-qubit operations on label 0, like a CNOT gate over labels 0 and 1.

Parameters
  • labels (tuple) – A tuple of integers representing the labels to be matched.

  • strict (bool) – Determines if label matching is performed by subset or by intersection.

  • exclusive (bool) – Whether to mark every operation yielded during iteration as no_more_noise. In any future iterations by any Match an operation with this marking will be skipped.

class trueq.simulation.match.GateMatch(gates, exclusive=False)

Instances of this class match a fixed set of gates. Operations are matched as follows:

  • Any gate in gates.

  • Any presented OpWrapper where no_more_noise is True is skipped.

Parameters
  • gates (Gate | Iterable) – A gate or iterable of gates.

  • exclusive (bool) – Whether to mark every operation yielded during iteration as no_more_noise. In any future iterations by any Match an operation with this marking will be skipped.

class trueq.simulation.match.CycleMatch(cycle, exclusive=False)

Instances of this class match all operations that come from a specified cycle. Operations are matched as follows:

  • All operations in a cycle if the cycle is equal to cycle.

  • Any presented OpWrapper where no_more_noise is True is skipped.

Parameters
  • cycle (Cycle) – A reference cycle for matching.

  • exclusive (bool) – Whether to mark every operation yielded during iteration as no_more_noise. In any future iterations by any Match an operation with this marking will be skipped.

class trueq.simulation.match.SingleQubitMatch(exclusive=False)

Instances of this class match operations that act on only a single qubit label. Operations are matched as follows:

  • All operations that act on a single qubit label.

  • Any presented OpWrapper where no_more_noise is True is skipped.

Parameters

exclusive (bool) – Whether to mark every operation yielded during iteration as no_more_noise. In any future iterations by any Match an operation with this marking will be skipped.

class trueq.simulation.match.R90Match(exclusive=False)

Instances of this class match qubit gates that can be represented as a 90 degree rotation about an axis within the XY plane. Operations are matched as follows:

  • Any qubit gate that is a 90 degree rotation about an axis within the XY plane.

  • Any presented OpWrapper where no_more_noise is True is skipped.

Parameters

exclusive (bool) – Whether to mark every operation yielded during iteration as no_more_noise. In any future iterations by any Match an operation with this marking will be skipped.

Raises

RuntimeError – Throws an error if the cycle is not from a qubit circuit.

class trueq.simulation.match.AssociativeMatch(*matches, exclusive=None)

Abstract parent class for children C that store a list of Matches and have the property that C([A, C([B, C])) and C([A, B, C]) share the same behaviour. This flattening process is performed automatically by the constructor when AssociativeMatches of the same type are encountered.

Parameters
  • matches (list) – A list of Matches of any type.

  • exclusive (bool | None) – Whether to mark every operation yielded during iteration as no_more_noise. In any future iterations by any Match an operation with this marking will be skipped.

class trueq.simulation.match.AndMatch(*matches, exclusive=None)

Instances of this class match operations that match the intersection of the given matches. Operations are matched as follows:

  • Any operation that matches all of the given matches.

  • Any presented OpWrapper where no_more_noise is True is skipped.

Parameters
  • matches (list) – A list of Matches of any type.

  • exclusive (bool | None) – Whether to mark every operation yielded during iteration as no_more_noise. In any future iterations by any Match an operation with this marking will be skipped.

class trueq.simulation.match.OrMatch(*matches, exclusive=None)

Instances of this class match operations that match the union of the specified set of matches. Operations are matched as follows:

  • Any operation that matches any of the given matches.

  • Any presented OpWrapper where no_more_noise is True is skipped.

Parameters
  • matches (list) – A list of Matches of any type.

  • exclusive (bool | None) – Whether to mark every operation yielded during iteration as no_more_noise. In any future iterations by any Match an operation with this marking will be skipped.

NoiseSource

class trueq.simulation.noise_source.NoiseSource(match=None, dim=None)

Parent class for all built in noise sources which can be added to the simulator. A noise source comprises a method apply() that mutates the state of simulation by inspecting a given cycle, a Match instance for filtering which operations in the cycle to consider, a cache for storing saved calculations that persists for the lifetime of the noise source, and an optional attribute dim for explicitly defining the allowed subsystem dimension.

Generally, a NoiseSource is added to a simulator, and then during circuit propagation it has an opportunity to apply noise cycle by cycle. Within the simulation of each cycle, a noise source can either simply add noise, or additionally attempt to simulate some of the operations within a cycle. A default noise source is always included in the Simulator to ensure that all gates within a cycle that are not simulated by a user append noise source are eventually simulated ideally.

Parameters
  • match (Match | None) – A match object which determines the conditions required for a noise source to attempt to apply itself to the state during simulation.

  • dim (int | None) – The subspace dimension expected by a noise source, or None, if there is no needed restriction to a particular subspace dimension.

property dim

Returns the Hilbert space dimension of each subsystem expected by this propagator. If the dimension is set to None, then the noise source does not care about the dimension of a given subsystem. Otherwise, the dimension is an integer, and the noise source can use this information to assert that it can be applied correctly during simulation.

Returns

The expected dimension of subsystems as an integer, or None.

Type

int | None

property match

The Match instance owned by this noise source. This object is typically used by apply() to filter which cycle operations are presented to it.

For example, the noise source can be initialized with a match on gates, defined by an instance of GateMatch initialized with a list of gates. In this case, the noise source will only have the opportunity to apply itself to the state whenever one of these gates appears.

Returns

The Match which determines the conditions required for a noise source to attempt to apply itself to the state during simulation.

Type

Match

make_circuit_cache(circuit)

Instantiates a cache object that will be made available to apply() as circuit_cache for each call within the circuit. The cache is typically a dictionary that stores information about the circuit that is needed by apply() but not available in individual cycles of the circuit, such as all of the labels the circuit acts on.

Parameters

circuit (Circuit) – The circuit being simulated by the (noisy) simulator.

Returns

A dictionary of circuit information.

Return type

dict

apply(cycle_wrapper, state, circuit_cache)

Applies all gates in the given cycle_wrapper that have not yet been simulated to the given state. This method defines ideal simulation, which children will typically overload to add noise.

Parameters
  • cycle_wrapper (CycleWrapper) – Current cycle being propagated by the simulator.

  • state (StateTensor | OperatorTensor) – The state of the simulation. This can either represent the quantum state of the simulated circuit thus far, or the operator which describes the action of the simulated circuit thus far.

  • circuit_cache (dict) – Cached information about the circuit.

class trueq.simulation.add_common.KrausNoise(kraus_ops, match=None)

A noise source that adds Kraus noise; see add_kraus() for details. On initialization, the constructor copies and reshapes the Kraus operators to prepare for direct application to the state as a superoperator.

Parameters
  • kraus_ops (list | dict) – Either a list-like of square matrices, each having the dimension of a single system; or, a dictionary whose keys are n_sys and whose values are list-likes of square matrices, specifying the Kraus operators for gates with that value of n_sys.

  • match (Match | None) – A match object which determines the conditions required for a noise source to attempt to apply itself to the state during simulation.

Raises

ValueError – If kraus_ops has the wrong dimension or is not a list of square matrices.

apply(cycle_wrapper, state, cache)

Applies the Kraus operators to the state on each label containing gates that match with match. If it encounters a multi qubit gate, and does not have Kraus operators specifically defined for the number of subspaces upon which the gate acts, then it will apply single qubit Kraus operators to each subsystem instead. This noise source does not attempt to simulate the gates in the cycle.

Parameters
  • cycle_wrapper (CycleWrapper) – The data structure containing the cycle that state is to be propagated by.

  • state (StateTensor | OperatorTensor) – The state to propagate.

  • cache (dict) – A dictionary to cache results in.

class trueq.simulation.add_common.OverrotationNoise(single_sys, multi_sys, match=None)

A noise source that adds overrotation, a unitary error; see add_overrotation() for details.

Parameters
  • single_sys (float) – The amount of overrotation to add to single qubit gates, computed by exponentiating the target gate by 1 + single_sys.

  • multi_sys (float) – The amount of overrotation to add to multi qubit gates, computed by exponentiating the target gate by 1 + multi_sys.

  • match (Match | None) – A match object which determines the conditions required for a noise source to attempt to apply itself to the state during simulation.

apply(cycle_wrapper, state, cache)

Applies all the gates that match with match to the state, with overrotation. Gates are cached on first application, and read from the cache thereafter.

Parameters
  • cycle_wrapper (CycleWrapper) – The data structure containing the cycle that state is to be propagated by.

  • state (StateTensor | OperatorTensor) – The state to propagate.

  • cache (dict) – A dictionary to cache results in.

class trueq.simulation.add_common.RelaxationNoise(t1, t2, t_single, t_multi, excited_pop=0, match=None)

A noise source that adds relaxation noise, a noise restricted to qubits; see add_relaxation() for details. Relaxation noise simulates a \(T_1\) and \(T_2\) process on the qubits, in an idealized manner assuming that gates happen instantaneously, and that the relaxation occurs before any ideal operation is simulated.

Parameters
  • t1 (float | dict) – The \(T_1\) time, i.e. the characteristic time of relaxation.

  • t2 (float | dict) – The \(T_2\) time, i.e. the characteristic time of dephasing.

  • t_single (float) – The idealized time it takes for single qubit cycles to run.

  • t_multi (float) – The idealized time it takes for multi qubit cycles to run.

  • excited_pop (float | dict) – The excited state population at equilibrium.

  • match (Match | None) – A match object which determines the conditions required for a noise source to attempt to apply itself to the state during simulation.

make_circuit_cache(circuit)

Instantiates a cache object that will be made available to apply() as circuit_cache for each call within the circuit. The cache is typically a dictionary that stores information about the circuit that is needed by apply() but not available in individual cycles of the circuit, such as all of the labels the circuit acts on.

A relaxation noise source will attempt to apply noise on all qubits present in the circuit, even if they do not have an operation acting on them during a particular cycle. Thus this cache contains all of the labels the circuit acts on.

Parameters

circuit (Circuit) – The circuit being simulated by the (noisy) simulator.

Returns

A dictionary of circuit information.

Return type

dict

static relaxation_channel(t1, t2, t, excited_pop)

Determines and returns the superoperator for a relaxation channel by computing the choi representation, and casting it to a superoperator.

Parameters
  • t1 (float | dict) – The \(T_1\) time, i.e. the characteristic time of relaxation.

  • t2 (float | dict) – The \(T_2\) time, i.e. the characteristic time of dephasing.

  • t (float) – The time that the channel is applied for.

  • excited_pop (float | dict) – The excited state population at equilibrium.

Return type

numpy.ndarray

Raises

ValueError – Raises when \(T_1\) and \(T_2\) are physically invalid, or if excited_pop is a not a valid probability.

apply(cycle_wrapper, state, cache)

Applies relaxation noise to all qubits in the circuit. If match finds any multi qubit gates within the cycle, the entire cycle takes t_multi amount of time. Otherwise, the cycle is assumed to take t_single amount of time.

Parameters
  • cycle – The cycle that state is to be propagated by.

  • cycle_wrapper (CycleWrapper) – The data structure containing the cycle that state is to be propagated by.

  • cache (dict) – A dictionary to cache results in.

class trueq.simulation.add_spam.PrepNoise(state, match=None)

A noise source that adds state preparation error; see add_prep() for details. A preparation noise source can indicate a dictionary of labels and target noisy states, a single target noisy state, or a probability representing a classically mixed state as the noisy target. Only the preparation objects that match with the condition(s) of match will be affected. If a single state/probability is provided, it will be stored with a None key, and will apply this noise source to all valid preparation object returned by match.

Parameters
  • state (dict | StateTensor | float) – A state or a dictionary of (labels, states) defining the state preparation error. A probability in place of a state represents a classically mixed state of the same probability.

  • match (Match | None) – A match object which determines the conditions required for a noise source to attempt to apply itself to the state during simulation.

Raises

ValueError – If dimensions are inconsistent during state preparation, or the bitflip probability is not a valid probability.

apply(cycle_wrapper, state, cache)

Applies the preparation noise to the state, by checking if each matched label from match corresponds to a label within the initialized states, or if None is encountered as a key, the preparation noise will be applied to all matched labels. The effective superoperator is the partial trace over matched subsystems, tensored together with states in the traced out subsystems.

Parameters
  • cycle_wrapper (CycleWrapper) – The data structure containing the cycle that state is to be propagated by.

  • state (StateTensor) – The state to propagate.

  • cache (dict) – A dictionary to cache results in.

class trueq.simulation.add_spam.POVMNoise(povm, match=None)

A noise source that adds POVM noise on a measurement; see add_povm() for details. Sine measurement can only occur during the last cycle of a circuit, this noise source will only attempt to apply itself during a measurement cycle.

Parameters
  • povm (Tensor) – A tensor describing a POVM.

  • match (Match | None) – A match object which determines the conditions required for a noise source to attempt to apply itself to the state during simulation.

Raises

NotImplementedError – If the match is not None.

apply(cycle_wrapper, state, cache)

Applies all gates in the given cycle_wrapper that have not yet been simulated to the given state. This method defines ideal simulation, which children will typically overload to add noise.

Parameters
  • cycle_wrapper (CycleWrapper) – Current cycle being propagated by the simulator.

  • state (StateTensor | OperatorTensor) – The state of the simulation. This can either represent the quantum state of the simulated circuit thus far, or the operator which describes the action of the simulated circuit thus far.

  • circuit_cache (dict) – Cached information about the circuit.

OpWrapper

class trueq.simulation.match.OpWrapper(labels, op)

Wraps labels and an Operation into one small object. The wrapper keeps track of whether or not it has been simulated via has_been_simulated, which is marked as True during cycle propagation within a simulation by a NoiseSource's Match when it simulates the action of the operation on the state. Further, it keeps track of whether some noise source has indicated that no further noise should be applied via no_more_noise, and is primarily modified by setting the exclusive variable defined in a Match to True.

Two OpWrappers are equal if and only if all four of their properties are equal. That is, the labels and operations must be equal, as well as the state of their flags, has_been_simulated and no_more_noise.

Parameters

CycleWrapper

class trueq.simulation.match.CycleWrapper(cycle)

Wraps a Cycle into a list of OpWrappers, partitioning the cycle ops into four groups, Block which contains all blocks, Gate which contains all gates, Meas which contains all measurements, and Prep which contains all state preparations in the cycle being propagated by the simulator. The wrapper also contains the Cycle itself, as well as its dimension via dim.

A CycleWrapper can be asked to return all gates which have not yet been simulated via final_iter(), serving as a final catch during cycle propagation, and is utilized by the parent class of noise sources, NoiseSource during the call of apply().

Parameters

cycle (Cycle) – The cycle being wrapped.