sandialabs / pyGSTi

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

Help with detecting crosstalk errors of simulated data #466

Open AxelVahur opened 1 month ago

AxelVahur commented 1 month ago

Hello,

As part of my master's project on crosstalk I'm using pyGSTi to simulate 2-qubit data and detect crosstalk in it, similar to https://arxiv.org/pdf/1908.09855v3 and eventually would like to detect crosstalk in the Finnish Helmi 5-qubit computer. Even after spending quite a bit of time I can't get the do_basic_crosstalk_detection function to work. Before getting to that I needed suitable circuits to simulate and attempted to use the crosstalk_detection_experiment2 function which gave errors until I changed a few lines in its definition. It then returned a reasonable looking crosstalk experiment dictionary and simulating the circuits in this dictionary worked fine.

Attempting to feed this DataSet object into the do_basic_crosstalk_detection function fails right away at the function trying to index list-generators instead of lists and changing the definition to use list() instead would give a keyerror "settings" on the next line. After that I decided to convert the dataset into a 2d numpy array myself, with each entry being a 4-element list of outcomes of region 0, region 1, settings on region 0, settings on region 1 and each of these is a list. From the article I understand that SPAM operations being the same, settings mean only the gates applied, i.e. circuits. The settings argument being a list [1, 1] was what satisfied the original np.shape checks for 2 qubits which produce 4 columns, but this is still not in the correct format.

All of these issues probably arise from not understanding how to input the settings despite reading the articles and documentation. Below are first my modified crosstalk_detection_experiment2 with the original lines commented out above my changes on lines 42, 77, 91, 95, 100, and finally my remaining code.

Windows 11 python 3.11.9 pyGSTi 0.9.12.3

import numpy as _np
import pygsti

def axel_crosstalk_detection_experiment2(pspec, lengths, circuits_per_length, circuit_population_sz, multiplier=3,
                                    idle_prob=0.1, structure='1Q',
                                    descriptor='A set of crosstalk detections experiments', verbosity=1):

    experiment_dict = {}
    experiment_dict['spec'] = {}
    experiment_dict['spec']['lengths'] = lengths
    experiment_dict['spec']['circuit_population_sz'] = circuit_population_sz
    experiment_dict['spec']['multiplier'] = multiplier
    experiment_dict['spec']['idle_prob'] = idle_prob
    experiment_dict['spec']['descriptor'] = descriptor
    experiment_dict['spec']['createdby'] = 'extras.crosstalk.crosstalk_detection_experiment2'

    if isinstance(structure, str):
        assert(structure == '1Q'), "The only default `structure` option is the string '1Q'"
        structure = tuple([(q,) for q in pspec.qubit_labels])
        n = pspec.num_qubits
    else:
        assert(isinstance(structure, list) or isinstance(structure, tuple)), \
            "If not a string, `structure` must be a list or tuple."
        qubits_used = []
        for subsetQs in structure:
            assert(isinstance(subsetQs, list) or isinstance(subsetQs, tuple)), "SubsetQs must be a list or a tuple!"
            qubits_used = qubits_used + list(subsetQs)
            assert(len(set(qubits_used)) == len(qubits_used)), \
                "The qubits in the tuples/lists of `structure must all be unique!"

        assert(set(qubits_used).issubset(set(pspec.qubit_labels))), \
            "The qubits to benchmark must all be in the QubitProcessorSpec `pspec`!"
        n = len(qubits_used)

    experiment_dict['spec']['circuits_per_length'] = circuits_per_length * multiplier * n

    experiment_dict['spec']['structure'] = structure
    experiment_dict['circuits'] = {}
    experiment_dict['settings'] = {}

    # gates_available = list(pspec.models['target'].primitive_op_labels)    #QubitProcessorSpec has no attribute models
    gates_available = list(pspec.primitive_op_labels)
    gates_by_qubit = [[] for _ in range(0, n)]
    for i in range(0, len(gates_available)):
        for q in range(0, n):
            if gates_available[i].qubits == (q,):
                gates_by_qubit[q].append(gates_available[i])

    for lnum, l in enumerate(lengths):
        # generate menu of circuits for each qubit
        circuit_menu = [[] for _ in range(0, n)]
        for q in range(0, n):
            d = len(gates_by_qubit[q])
            if d**l < circuit_population_sz:
                print(('- Warning: circuit population specified too large for qubit {}'
                        ' -- there will be redundant circuits').format(q))
            for rep in range(0, circuit_population_sz):
                singleQcirc = []
                for j in range(0, l): #creates an l-length single qubit circuit
                    r = _np.random.randint(0, d)
                    singleQcirc.append(gates_by_qubit[q][r])
                circuit_menu[q].append(singleQcirc)

        cnt = 0
        for q in range(0, n):
            print('  - Qubit {} = '.format(q))

            # need circuits_per_length number of settings for this qubit
            for j in range(circuits_per_length):
                #  iteratively choose each of the circuits in the menu
                qr = j

                # generate "multiplier" number of random circuits on the other qubits with qr setting
                #  on the central qubit
                for m in range(0, multiplier):
                    # circuit = _Circuit(num_lines=0, editable=True)
                    circuit = pygsti.circuits.Circuit(num_lines=0, editable=True)
                    settings = {}

                    for q1 in range(0, n):

                        if q1 == q:
                            # the setting for the central qubit is fixed
                            r = qr
                        else:
                            # draw a setting
                            r = _np.random.randint(0, circuit_population_sz)

                        settings[(q1,)] = lnum * (circuit_population_sz + 1) + r + 1
                        # singleQcircuit = _Circuit(num_lines=1, line_labels=[q1], editable=True)
                        singleQcircuit = pygsti.circuits.Circuit(num_lines=1, line_labels=[q1], editable=True)
                        for layer in range(0, l):
                            # singleQcircuit.insert_layer(circuit_menu[q1][r][layer], layer)    # returns a Circuit but not assigned,
                            # leaving singleQcircuit with 0 layers and second loop gives index error
                            singleQcircuit = singleQcircuit.insert_layer(circuit_menu[q1][r][layer], layer)

                        singleQcircuit.done_editing()

                        # circuit.tensor_circuit(singleQcircuit)    # also not assigned
                        circuit = circuit.tensor_circuit(singleQcircuit)

                    experiment_dict['settings'][l, cnt] = settings
                    # for each line, except the central qubit, replace sequence with an idle
                    #  independently according to idle_prob
                    if idle_prob > 0:
                        for q1 in range(0, n):
                            if q1 != q:
                                idle = bool(_np.random.binomial(1, idle_prob))
                                if idle:
                                    circuit.replace_with_idling_line_inplace(q1)
                                    # Update the setting on that qubit to the idling setting
                                    #  (denoted by the length index)
                                    experiment_dict['settings'][l, cnt][(q1,)] = lnum * (circuit_population_sz + 1)
                                    if verbosity > 0: print('(Idled {}) '.format(q1), end='')

                    circuit.done_editing()
                    experiment_dict['circuits'][l, cnt] = circuit
                    cnt += 1

                    if verbosity > 0: print('{}, '.format(m), end='')
                if verbosity > 0: print(')')
        print('cnt: {}'.format(cnt))
    return experiment_dict
import pygsti
import pygsti.extras.crosstalk as crosstalk
import numpy as np

processorSpec = pygsti.processors.QubitProcessorSpec(2, ["Gxpi2", "Gypi2", "Gi"], geometry = "line")
mdlCn = pygsti.models.create_cloud_crosstalk_model(processorSpec,
    lindblad_error_coeffs={
        ("Gxpi2",0): {("S","X:1"): 0.1} #"H", "S" Hamiltonian and Pauli-Stochastic errors
    },
    simulator="map"
)

ctDict = axel_crosstalk_detection_experiment2(processorSpec, [4], 10, 20, verbosity=0, multiplier=3)
circuitList = list(ctDict["circuits"].values())
tdds = pygsti.data.datasetconstruction.simulate_data(mdlCn, circuitList, num_samples=100, sample_error='multinomial', seed=557, times=[0.0])

print("circuitList: ", circuitList)
print("settings: ", ctDict["settings"])

aggre0 = pygsti.data.datasetconstruction.aggregate_dataset_outcomes(tdds, {"0":["00","01"],"1":["10","11"]}) #outcomes of qubit 0
aggre1 = pygsti.data.datasetconstruction.aggregate_dataset_outcomes(tdds, {"0":["00","10"],"1":["11","01"]}) #outcomes of qubit 1

print("row1 of dataset: ", tdds[circuitList[0]])
print("row1 of qubit0: ", aggre0[circuitList[0]])
print("row1 of qubit1: ", aggre1[circuitList[0]])

myList = []
mySettings = [1, 1]

for i in range(0, len(circuitList), 1):
    tempCircuit = circuitList[i]    #pick ith circuit from circuitList
    expList = []

    q0outcomeList = list(aggre0[tempCircuit])
    count0 = list(q0outcomeList[0])[2]
    count1 = list(q0outcomeList[1])[2]
    q0arr = [count0, count1]
    expList.append(q0arr)

    q1outcomeList = list(aggre1[tempCircuit])
    count0 = list(q1outcomeList[0])[2]
    count1 = list(q1outcomeList[1])[2]
    q1arr = [count0, count1]
    expList.append(q1arr)

    expList.append(list(tempCircuit)) # settings of region 0
    expList.append(list(tempCircuit)) # settings of region 1 (same)
    myList.append(expList)

npList = np.array(myList, dtype=object)

print("\n", myList[0])
print("\n", npList[0])

crosstalk.do_basic_crosstalk_detection(npList, 2, [1,1])