Circuit Simulation#
This tutorial demonstrates how to compute (simulate) the outcome probabilities of circuits in pyGSTi. There are currently two basic ways to to this - but constructing and simulating a Circuit object, or by constructing and propagating a state.
Method 1: Circuit simulation#
This is the primary way circuit simulation is done in pyGSTi. Model objects are statistical models that predict the outcome probabilities of events, and (at least for all current model types) “events” are circuits, described by Circuit objects. Thus, the three steps to simulating a circuit using this approach are:
create a
Modelcreate a
Circuitcall
model.probabilities(circuit)Building models and circuits (steps 1 and 2) are largely covered in other tutorials (see the essential objects tutorial, circuits tutorial, and explicit-op model and implicit-op model tutorials). This section focuses on step 3 andModeloptions which impact the way in which a model computes probabilities. This approach to circuit simulation is most convenient when you have a large number of circuits which are known (and fixed) beforehand.
Let’s begin with a simple example, essentially the same as the one in the using-essential-objects tutorial:
import pygsti
mdl = pygsti.models.create_explicit_model_from_expressions((0,1),
[(), ('Gxpi2',0), ('Gypi2',0), ('Gxpi2',1), ('Gypi2',1), ('Gcnot',0,1)],
["I(0,1)","X(pi/2,0)", "Y(pi/2,0)", "X(pi/2,1)", "Y(pi/2,1)", "CNOT(0,1)"])
c = pygsti.circuits.Circuit([('Gxpi2',0),('Gcnot',0,1),('Gypi2',1)] , line_labels=[0,1])
print(c)
mdl.probabilities(c) # Compute the outcome probabilities of circuit `c`
This example builds an ExplicitOpModel (best for 1-2 qubits) on 2 qubits with \(X(\pi/2)\) and \(Y(\pi/2)\) rotation gates on each qubit and a CNOT gate between them. This model is able to simulate any circuit layer (a.k.a. “time-step” or “clock-cycle”) that contains any one of these gates (this is what it means to be an explicit-op model: the operation for every simulate-able circuit layer must be explicitly supplied to the Model). For example, this model cannot simulate a circuit layer where two Gxpi2 gates occur in parallel:
c2 = pygsti.circuits.Circuit([ [('Gxpi2',0), ('Gxpi2',1)],('Gcnot',0,1) ] , line_labels=[0,1])
print(c2)
try:
mdl.probabilities(c2)
except KeyError as e:
print("KEY ERROR (can't simulate this layer): " + str(e))
As is detailed in the implicit-op model tutorial, an “implicit-operation” model is able to implicitly create layer operations from constituent gates, and thus perform the simulation of c2:
pspec = pygsti.processors.QubitProcessorSpec(2, ('Gxpi2', 'Gypi2', 'Gcnot'), geometry='line')
implicit_mdl = pygsti.models.create_crosstalk_free_model(pspec)
print(c2)
implicit_mdl.probabilities(c2)
Method 2: state propagation#
In this method of circuit simulation, a state object (a SPAMVec in pyGSTi) is propagated circuit-layer by circuit-layer. This method is convenient when a there are few (or just one!) circuit that involves substantial classical logic or needs to be probed at various points in time. It is slower to simulate circuits in this way, as it requires calls more calls between pyGSTi’s Python and C routines than method 1 does.
The two cells below show how to perform the same two circuits above using the state-propagation method.
#Simulating circuit `c` above using `mdl`: [('Gxpi2',0),('Gcnot',0,1),('Gypi2',1)]
rho = mdl['rho0']
rho = mdl[('Gxpi2',0)].acton(rho)
rho = mdl[('Gcnot',0,1)].acton(rho)
rho = mdl[('Gypi2',1)].acton(rho)
probs = mdl['Mdefault'].acton(rho)
print(probs)
Note that, especially for implicit models, the interface is a bit clunky.
Warning
Simulation by state propagation is a work in progress in pyGSTi, and users should expect that this interface may change (improve!) in the future.
#Simulating circuit `c2` above using `implicit_mdl`: [ [('Gxpi2',0), ('Gxpi2',1)], ('Gcnot',0,1) ]
from pygsti.baseobjs import Label as L
rho = implicit_mdl.prep_blks['layers'][L('rho0')]
rho = implicit_mdl.operation_blks['layers'][ L('Gxpi2',0) ].acton(rho)
rho = implicit_mdl.operation_blks['layers'][ L('Gxpi2',1) ].acton(rho)
rho = implicit_mdl.operation_blks['layers'][ L('Gcnot',(0,1)) ].acton(rho)
probs = implicit_mdl.povm_blks['layers']['Mdefault'].acton(rho)
print(probs)
Forward-simulation types#
PyGSTi refers to the process of computing circuit-outcome probabilities as forward simulation, and there are several methods of forward simulation currently available. The current default method (selected when simulator="auto") is "map", which performs repeated matrix-vector products on the state representation (operations are treated as abstract maps). The "matrix" method — which multiplies together dense process matrices for the entire circuit — is also available and can be useful for small (1–2 qubit) Hilbert spaces where dense-matrix caching pays off across very large circuit batches. A Model’s active forward simulator is held by its .sim attribute (an instance of a ForwardSimulator subclass) and is set at construction time. For more information on using different types of forward simulation see the forward simulation types tutorial.
Here are some examples showing which method is being used and how to switch between them. Usually you don’t need to worry about the forward-simulation type, but in the future pyGSTi may have more options for specialized purposes.
c3 = pygsti.circuits.Circuit([('Gxpi2',0),('Gcnot',0,1)] , line_labels=[0,1])
explicit_mdl = pygsti.models.create_explicit_model_from_expressions((0,1),
[(), ('Gxpi2',0), ('Gypi2',0), ('Gxpi2',1), ('Gypi2',1), ('Gcnot',0,1)],
["I(0,1)","X(pi/2,0)", "Y(pi/2,0)", "X(pi/2,1)", "Y(pi/2,1)", "CNOT(0,1)"])
print("2Q explicit_mdl will simulate probabilities using the '%s' forward-simulation method." % explicit_mdl.sim)
explicit_mdl.probabilities(c3)
pspec = pygsti.processors.QubitProcessorSpec(3, ('Gxpi2', 'Gypi2', 'Gcnot'), geometry='line')
implicit_mdl = pygsti.models.create_crosstalk_free_model(pspec)
print("3Q implicit_mdl will simulate probabilities using the '%s' forward-simulation method." % implicit_mdl.sim)
implicit_mdl.probabilities(c)
implicit_mdl.sim = 'matrix'
print("3Q implicit_mdl will simulate probabilities using the '%s' forward-simulation method." % implicit_mdl.sim)
implicit_mdl.probabilities(c)