sandialabs / pyGSTi

A python implementation of Gate Set Tomography
http://www.pygsti.info
Apache License 2.0
132 stars 55 forks source link

RBDesign bugfixes and improvements #443

Closed sserita closed 1 month ago

sserita commented 1 month ago

Fixes several RB issues. I still plan to write a few tests for these, but it is at least ready to review from the RB codeowners to ensure that these are good solutions to the features.

Deterministic Clifford compilation (#314)

The desire was to be able to do deterministic Clifford compilation when generating CliffordRBDesign objects. We actually already had a partial way to do this because the CliffordCompilationRules are more or less exactly this - a rule for how to build up a circuit from native gates that implement a Clifford. So my implementation is just to add a exact_compilation_key kwarg which uses the compilation rules to do this deterministic compilation when provided, and otherwise to do the previous behavior of synthesizing using the random algorithms.

An example:

import pygsti
from pygsti.processors import QubitProcessorSpec as QPS
from pygsti.processors import CliffordCompilationRules as CCR

n_qubits = 1
qubit_labels = ['Q'+str(i) for i in range(n_qubits)] 
gate_names = ['Gi', 'Gxpi2', 'Gxpi', 'Gxmpi2', 'Gypi2', 'Gypi', 'Gympi2', 
              'Gzpi2', 'Gzpi', 'Gzmpi2', 'Gcphase']
availability = {'Gcphase':[('Q'+str(i),'Q'+str((i+1) % n_qubits)) for i in range(n_qubits)]}
pspec = QPS(n_qubits, gate_names, availability=availability, qubit_labels=qubit_labels)
compilations = {'absolute': CCR.create_standard(pspec, 'absolute', ('paulis', '1Qcliffords'), verbosity=0),            
                'paulieq': CCR.create_standard(pspec, 'paulieq', ('1Qcliffords', 'allcnots'), verbosity=0)}
depths = [0, 2]
k = 10
qubits = qubit_labels

This will create the design as before, with random Clifford synthesis:

design = pygsti.protocols.CliffordRBDesign(pspec, compilations, depths, k, qubit_labels=qubits, 
                                           randomizeout=False, seed=20240522)

This will create a design using the absolute compilation of each Clifford instead:

design = pygsti.protocols.CliffordRBDesign(pspec, compilations, depths, k, qubit_labels=qubits, 
                                           randomizeout=False, seed=20240522, exact_compilation_key="absolute")

Or similarly, we could do a compilation up to Pauli equivalence with the paulieq compilation:

design = pygsti.protocols.CliffordRBDesign(pspec, compilations, depths, k, qubit_labels=qubits, 
                                           randomizeout=False, seed=20240522, exact_compilation_key="paulieq")

A non-standard compilation rule could also be done:

compilations["nonstd"] = pygsti.processors.CompilationRules({some dict of clifford names -> circuit templates})
design = pygsti.protocols.CliffordRBDesign(pspec, compilations, depths, k, qubit_labels=qubits, 
                                           randomizeout=False, seed=20240522, exact_compilation_key="nonstd")

Native gate per Clifford statistics (#315)

The CliffordRBDesign now keeps track of how many native gates are in the first length+1 Cliffords, i.e. excluding the inversion Clifford, stores them in a native_gate_count_lists member variable, and provides convenience functions to compute the averages.

design = pygsti.protocols.CliffordRBDesign(pspec, compilations, [0, 2], 5, qubit_labels=qubits, 
                                           randomizeout=False, citerations=10)
design.native_gate_count_lists # A list of lists of the number of native gates for the compiled Cliffords (without inversion)
design.average_native_gates_per_clifford()  # Compute average over all circuits

As an aside, I also added a num_gates utility function to Circuit that was apparently missing. There was num_nq_gates and num_multiq_gates so you could have done num_nq_gates(1) + num_multiq_gates, but might as well provide it.

Truncation bugfix for RBDesigns (#408)

The various BenchmarkingDesign classes often have lists that are intended to be paired with the circuit_lists member attribute. These paired attributes were not being truncated properly.

To fix this, the BenchmarkingDesign now has a paired_with_circuit_attrs member variable which lists the names of any attribute that is intended to correspond 1-to-1 with the circuit_lists. During truncation, these paired attributes are zipped up with the circuits during filtering, and then unzipped into the new truncated lists. Paired attributes are also serialized separately to JSON files, as we often want to look at them independently.

A currently exhaustive list of paired attributes (paired attributes propogate to derived classes):