Example #1
0
 def test_construction_label_conversion(self):
     # XXX what is tested here that is not covered by other tests?  EGN: this is more of a use case for when this input is a *nested* tuple.
     # Check that parallel operation labels get converted to circuits properly
     opstr = circuit.Circuit(((('Gx', 0), ('Gy', 1)), ('Gcnot', 0, 1)))
     c = circuit.Circuit(layer_labels=opstr, num_lines=2)
     self.assertEqual(c._labels, (Label((('Gx', 0), ('Gy', 1))), Label('Gcnot', (0, 1))))
Example #2
0
    def test_simulate_marginalization(self):
        pspec = QubitProcessorSpec(4, ['Gx', 'Gy'], geometry='line')
        mdl = mc.create_crosstalk_free_model(pspec)

        #Same circuit with different line labels
        c = circuit.Circuit("Gx:0Gy:0", line_labels=(0,1,2,3))
        cp = circuit.Circuit("Gx:0Gy:0", line_labels=(1,2,0,3))
        c01 = circuit.Circuit("Gx:0Gy:0", line_labels=(0,1))
        c10 = circuit.Circuit("Gx:0Gy:0", line_labels=(1,0))
        c0 = circuit.Circuit("Gx:0Gy:0", line_labels=(0,))

        #Make sure mdl.probabilities and circuit.simulate give us the correct answers
        pdict = mdl.probabilities(c)
        self.assertEqual(len(pdict), 16)  # all of 0000 -> 1111
        self.assertAlmostEqual(pdict['0000'], 0.5)
        self.assertAlmostEqual(pdict['1000'], 0.5)

        pdict = mdl.probabilities(cp)
        self.assertEqual(len(pdict), 16)  # all of 0000 -> 1111
        self.assertAlmostEqual(pdict['0000'], 0.5)
        self.assertAlmostEqual(pdict['0010'], 0.5)

        pdict = mdl.probabilities(c01)
        self.assertEqual(len(pdict), 4)  # all of 00 -> 11
        self.assertAlmostEqual(pdict['00'], 0.5)
        self.assertAlmostEqual(pdict['10'], 0.5)

        pdict = mdl.probabilities(c10)
        self.assertEqual(len(pdict), 4)  # all of 00 -> 11
        self.assertAlmostEqual(pdict['00'], 0.5)
        self.assertAlmostEqual(pdict['01'], 0.5)

        pdict = mdl.probabilities(c0)
        self.assertEqual(len(pdict), 2)  # all of 0 -> 1
        self.assertAlmostEqual(pdict['0'], 0.5)
        self.assertAlmostEqual(pdict['1'], 0.5)

        ## SAME results from circuit.simulate, except with smaller dicts (because 0s are dropped)
        pdict = c.simulate(mdl)
        self.assertEqual(len(pdict), 2)
        self.assertAlmostEqual(pdict['0000'], 0.5)
        self.assertAlmostEqual(pdict['1000'], 0.5)

        pdict = cp.simulate(mdl)
        self.assertEqual(len(pdict), 2)
        self.assertAlmostEqual(pdict['0000'], 0.5)
        self.assertAlmostEqual(pdict['0010'], 0.5)

        pdict = c01.simulate(mdl)
        self.assertEqual(len(pdict), 2)
        self.assertAlmostEqual(pdict['00'], 0.5)
        self.assertAlmostEqual(pdict['10'], 0.5)

        pdict = c10.simulate(mdl)
        self.assertEqual(len(pdict), 2)
        self.assertAlmostEqual(pdict['00'], 0.5)
        self.assertAlmostEqual(pdict['01'], 0.5)

        pdict = c0.simulate(mdl)
        self.assertEqual(len(pdict), 2)
        self.assertAlmostEqual(pdict['0'], 0.5)
        self.assertAlmostEqual(pdict['1'], 0.5)
Example #3
0
 def test_twoQgate_count(self):
     self.assertEqual(self.c.two_q_gate_count(), 0)
     labels = circuit.Circuit(None, stringrep="[Gcnot:Q0:Q1]^2[Gy:Q0Gx:Q1]Gi:Q0Gi:Q1")
     c = circuit.Circuit(layer_labels=labels, line_labels=['Q0', 'Q1'])
     self.assertEqual(c.two_q_gate_count(), 2)
Example #4
0
 def test_multiQgate_count(self):
     self.assertEqual(self.c.num_multiq_gates, 0)
     labels = circuit.Circuit(None, stringrep="[Gccnot:Q0:Q1:Q2]^2[Gccnot:Q0:Q1]Gi:Q0Gi:Q1")
     c = circuit.Circuit(layer_labels=labels, line_labels=['Q0', 'Q1', 'Q2'])
     self.assertEqual(c.num_multiq_gates, 3)
Example #5
0
def import_rb_summary_data(filename, numqubits, datatype='auto', verbosity=1):
    """
    todo

    """
    try:
        with open(filename, 'r') as f:
            if verbosity > 0: print("Importing " + filename + "...", end='')
    except:
        raise ValueError(
            "Date import failed! File does not exist or the format is incorrect."
        )

    aux = []
    descriptor = ''
    # Work out the type of data we're importing
    with open(filename, 'r') as f:
        for line in f:

            if (len(line) == 0 or line[0] != '#'): break

            elif line.startswith("# "):
                descriptor += line[2:]

            elif line.startswith("## "):

                line = line.strip('\n')
                line = line.split(' ')
                del line[0]

                if line[0:2] == ['rblength', 'success_probabilities']:

                    auxind = 2
                    if datatype == 'auto':
                        datatype = 'success_probabilities'
                    else:
                        assert(datatype == 'success_probabilities'), "The data format appears to be " + \
                            "success probabilities!"

                elif line[0:3] == [
                        'rblength', 'success_counts', 'total_counts'
                ]:

                    auxind = 3
                    if datatype == 'auto':
                        datatype = 'success_counts'
                    else:
                        assert (
                            datatype == 'success_counts'
                        ), "The data format appears to be success counts!"

                elif line[0:numqubits + 2] == [
                        'rblength',
                ] + ['hd{}c'.format(i) for i in range(numqubits + 1)]:

                    auxind = numqubits + 2
                    if datatype == 'auto':
                        datatype = 'hamming_distance_counts'
                    else:
                        assert(datatype == 'hamming_distance_counts'), "The data format appears to be Hamming " + \
                            "distance counts!"

                elif line[0:numqubits + 2] == [
                        'rblength',
                ] + ['hd{}p'.format(i) for i in range(numqubits + 1)]:

                    auxind = numqubits + 2
                    if datatype == 'auto':
                        datatype = 'hamming_distance_probabilities'
                    else:
                        assert(datatype == 'hamming_distance_probabilities'), "The data format appears to be " + \
                            "Hamming distance probabilities!"

                else:
                    raise ValueError("Invalid file format!")

                if len(line) > auxind:
                    assert (line[auxind] == '#')
                    if len(line) > auxind + 1:
                        auxlabels = line[auxind + 1:]
                else:
                    auxlabels = []

                break

    # Prepare an aux dict to hold any auxillary data
    aux = {key: {} for key in auxlabels}

    # Read in the data, using a different parser depending on the data type.
    if datatype == 'success_counts':

        success_counts = {}
        total_counts = {}
        finitecounts = True
        hamming_distance_counts = None

        with open(filename, 'r') as f:
            for line in f:
                if (len(line) > 0 and line[0] != '#'):

                    line = line.strip('\n')
                    line = line.split(' ')
                    l = int(line[0])

                    if l not in success_counts:
                        success_counts[l] = []
                        total_counts[l] = []
                        for key in auxlabels:
                            aux[key][l] = []

                    success_counts[l].append(float(line[1]))
                    total_counts[l].append(float(line[2]))

                    if len(aux) > 0:
                        assert (
                            line[3] == '#'
                        ), "Auxillary data must be divided from the core data!"
                    for i, key in enumerate(auxlabels):
                        if key != 'target' and key != 'circuit':
                            aux[key][l].append(_ast.literal_eval(line[4 + i]))
                        else:
                            if key == 'target':
                                aux[key][l].append(line[4 + i])
                            if key == 'circuit':
                                aux[key][l].append(_cir.Circuit(line[4 + i]))

    elif datatype == 'success_probabilities':

        success_counts = {}
        total_counts = None
        finitecounts = False
        hamming_distance_counts = None

        with open(filename, 'r') as f:
            for line in f:
                if (len(line) > 0 and line[0] != '#'):

                    line = line.strip('\n')
                    line = line.split(' ')
                    l = int(line[0])

                    if l not in success_counts:
                        success_counts[l] = []
                        for key in auxlabels:
                            aux[key][l] = []

                    success_counts[l].append(float(line[1]))

                    if len(aux) > 0:
                        assert (
                            line[2] == '#'
                        ), "Auxillary data must be divided from the core data!"
                    for i, key in enumerate(auxlabels):
                        if key != 'target' and key != 'circuit':
                            aux[key][l].append(_ast.literal_eval(line[3 + i]))
                        else:
                            if key == 'target':
                                aux[key][l].append(line[3 + i])
                            if key == 'circuit':
                                aux[key][l].append(_cir.Circuit(line[3 + i]))

    elif datatype == 'hamming_distance_counts' or datatype == 'hamming_distance_probabilities':

        hamming_distance_counts = {}
        success_counts = None
        total_counts = None

        if datatype == 'hamming_distance_counts': finitecounts = True
        if datatype == 'hamming_distance_probabilities': finitecounts = False

        with open(filename, 'r') as f:
            for line in f:
                if (len(line) > 0 and line[0] != '#'):

                    line = line.strip('\n')
                    line = line.split(' ')
                    l = int(line[0])

                    if l not in hamming_distance_counts:
                        hamming_distance_counts[l] = []
                        for key in auxlabels:
                            aux[key][l] = []

                    hamming_distance_counts[l].append(
                        [float(line[1 + i]) for i in range(0, numqubits + 1)])

                    if len(aux) > 0:
                        assert (
                            line[numqubits + 2] == '#'
                        ), "Auxillary data must be divided from the core data!"
                    for i, key in enumerate(auxlabels):
                        if key != 'target' and key != 'circuit':
                            aux[key][l].append(
                                _ast.literal_eval(line[numqubits + 3 + i]))
                        else:
                            if key == 'target':
                                aux[key][l].append(line[numqubits + 3 + i])
                            if key == 'circuit':
                                aux[key][l].append(line[numqubits + 3 + i])
                                #aux[key][l].append(_cir.Circuit(line[numqubits + 3 + i]))
    else:
        raise ValueError(
            "The data format couldn't be extracted from the file!")

    rbdataset = _dataset.RBSummaryDataset(
        numqubits,
        success_counts=success_counts,
        total_counts=total_counts,
        hamming_distance_counts=hamming_distance_counts,
        aux=aux,
        finitecounts=finitecounts,
        descriptor=descriptor)

    if verbosity > 0:
        print('complete')

    return rbdataset
Example #6
0
 def test_replace_gatename_inplace(self):
     # Test changing a gate name
     self.c.replace_gatename_inplace('Gx', 'Gz')
     labels = circuit.Circuit(None, stringrep="[Gz:Q0Gy:Q1]^2[Gy:Q0Gz:Q1]Gi:Q0Gi:Q1")
     c2 = circuit.Circuit(layer_labels=labels, line_labels=['Q0', 'Q1'])
     self.assertEqual(self.c, c2)
Example #7
0
 def test_append_circuit(self):
     # Test appending
     c2 = circuit.Circuit(layer_labels=[Label('Gx', 'Q0')], line_labels=['Q0', ], editable=True)
     self.c.append_circuit_inplace(c2)
     self.assertEqual(self.c.depth, 6)
     self.assertEqual(self.c[5, 'Q0'], Label('Gx', 'Q0'))
Example #8
0
 def test_construct_from_label_without_qubit_labels(self):
     # Check we can read-in a circuit that has no qubit labels: enforces them to be on
     # all of the lines.
     labels = circuit.Circuit(None, stringrep="Gx^2GyGxGi")
     c = circuit.Circuit(layer_labels=labels, num_lines=1)
     self.assertEqual(c.depth, 5)
Example #9
0
 def setUp(self):
     self.labels = circuit.Circuit(None, stringrep="[Gx:Q0Gy:Q1]^2[Gy:Q0Gx:Q1]Gi:Q0Gi:Q1")
     self.c = circuit.Circuit(layer_labels=self.labels, line_labels=['Q0', 'Q1'], editable=True)
Example #10
0
 def test_insert_circuit_with_qubit_subset(self):
     # Test inserting a circuit that is on *less* qubits.
     c2 = circuit.Circuit(layer_labels=[Label('Gx', 'Q0')], line_labels=['Q0', ])
     self.c.insert_circuit_inplace(c2, 1)
     self.assertEqual(self.c.line_labels, ('Q0', 'Q1'))
     self.assertEqual(self.c.num_lines, 2)
Example #11
0
 def test_replace_with_idling_line(self):
     c = circuit.Circuit([('Gcnot', 0, 1)], editable=True)
     c.replace_with_idling_line_inplace(0)
     self.assertEqual(c.tup, ((),) + ('@', 0, 1))
     self.assertEqual(c, ((),))
Example #12
0
 def test_empty_tuple_makes_idle_layer(self):
     c = circuit.Circuit(['Gi', Label(())])
     self.assertEqual(len(c), 2)
Example #13
0
 def __delitem__(self, circuit):
     if not isinstance(circuit, _cir.Circuit):
         circuit = _cir.Circuit(circuit)
     del self._info[self.cirIndex[circuit]]
     del self.cirIndex[circuit]
Example #14
0
 def test_construct_from_oplabels(self):
     # Now repeat the read-in with no parallelize, but a list of lists of oplabels
     labels = [[Label('Gi', 'Q0'), Label('Gp', 'Q8')], [Label('Gh', 'Q1'), Label('Gp', 'Q12')]]
     c = circuit.Circuit(layer_labels=labels, line_labels=['Q0', 'Q1', 'Q8', 'Q12'])
     self.assertLess(0, c.depth)
Example #15
0
 def test_prefix_circuit(self):
     c2 = circuit.Circuit(layer_labels=[Label('Gx', 'Q0')], line_labels=['Q0', ], editable=True)
     self.c.prefix_circuit_inplace(c2)
     self.assertEqual(self.c.depth, 6)
     self.assertEqual(self.c[0, 'Q0'], Label('Gx', 'Q0'))
Example #16
0
 def setUp(self):
     self.s1 = circuit.Circuit(('Gx', 'Gx'), stringrep="Gx^2")
     self.s2 = circuit.Circuit(self.s1, stringrep="Gx^2")
Example #17
0
 def test_tensor_circuit_with_longer(self):
     # Test tensoring circuits where the inserted circuit is shorter
     gatestring2 = circuit.Circuit(None, stringrep="[Gx:Q2Gy:Q3]^2[Gy:Q2Gx:Q3]Gi:Q2Gi:Q3Gy:Q2")
     c2 = circuit.Circuit(layer_labels=gatestring2, line_labels=['Q2', 'Q3'])
     self.c.tensor_circuit_inplace(c2)
     self.assertEqual(self.c.depth, max(self.c.depth, c2.depth))
Example #18
0
 def test_compress_expand(self):
     s1 = circuit.Circuit(('Gx', 'Gx'), stringrep="Gx^2")
     c1 = circuit.CompressedCircuit(s1)
     s1_expanded = c1.expand()
     self.assertEqual(s1, s1_expanded)
Example #19
0
def create_mirror_circuit(circ, pspec, circ_type='clifford+zxzxz'):
    """

    circ_type : clifford+zxzxz, cz(theta)+zxzxz
    """

    n = circ.width
    d = circ.depth

    pauli_labels = ['I', 'X', 'Y', 'Z']
    qubits = circ.line_labels

    _, gate_inverse = pspec.compute_one_qubit_gate_relations()
    gate_inverse.update(
        pspec.compute_multiqubit_inversion_relations())  # add multiQ inverse

    assert (circ_type in (
        'clifford+zxzxz',
        'cz(theta)+zxzxz')), '{} not a valid circ_type!'.format(circ_type)

    def compute_gate_inverse(gate_label):

        if gate_label.name in gate_inverse.keys():
            return _lbl.Label(gate_inverse[gate_label.name], gate_label.qubits)
        else:
            if gate_label.name == 'Gzr' or gate_label.name == 'Gczr':
                return _lbl.Label(gate_label.name,
                                  gate_label.qubits,
                                  args=(str(-1 * float(gate_label.args[0])), ))
            else:
                raise ValueError("Cannot invert gate with name {}".format(
                    gate_label.name))

    srep_dict = _symp.compute_internal_gate_symplectic_representations(
        gllist=['I', 'X', 'Y', 'Z'])
    # the `callable` part is a workaround to remove gates with args, defined by functions.
    srep_dict.update(
        pspec.compute_clifford_symplectic_reps(
            tuple((gn for gn, u in pspec.gate_unitaries.items()
                   if not callable(u)))))

    if 'Gxpi2' in pspec.gate_names:
        xname = 'Gxpi2'
    elif 'Gc16' in pspec.gate_names:
        xname = 'Gc16'
    else:
        raise ValueError(
            ("There must be an X(pi/2) gate in the processor spec's gate set,"
             " and it must be called Gxpi2 or Gc16!"))

    assert('Gzr' in pspec.gate_names), \
        "There must be an Z(theta) gate in the processor spec's gate set, and it must be called Gzr!"
    zrotname = 'Gzr'

    if circ_type == 'cz(theta)+zxzxz':
        assert('Gczr' in pspec.gate_names), \
            "There must be an controlled-Z(theta) gate in the processor spec's gate set, and it must be called Gczr!"
        czrotname = 'Gczr'

    Xpi2layer = [_lbl.Label(xname, q) for q in qubits]

    #make an editable copy of the circuit to add the inverse on to
    c = circ.copy(editable=True)

    #build the inverse
    d_ind = 0
    while d_ind < d:
        layer = circ.layer(d - d_ind - 1)
        if len(layer) > 0 and layer[
                0].name == zrotname:  # ask if it's a Zrot layer.
            # It's necessary for the whole layer to have Zrot gates
            #get the entire arbitrary 1q unitaries: Zrot-Xpi/2-Zrot-Xpi/2-Zrot
            current_layers = circ[d - d_ind - 5:d - d_ind]
            #recompile inverse of current layer
            for i in range(n):
                if n == 1:
                    old_params = [(float(current_layers[0].args[0]),
                                   float(current_layers[2].args[0]),
                                   float(current_layers[4].args[0]))
                                  for i in range(n)]
                else:
                    old_params = [(float(current_layers[0][i].args[0]),
                                   float(current_layers[2][i].args[0]),
                                   float(current_layers[4][i].args[0]))
                                  for i in range(n)]
                layer_new_params = [
                    _comp.inv_recompile_unitary(*p) for p in old_params
                ]
                theta1_layer = [
                    _lbl.Label(zrotname,
                               qubits[i],
                               args=(str(layer_new_params[i][0]), ))
                    for i in range(len(layer_new_params))
                ]
                theta2_layer = [
                    _lbl.Label(zrotname,
                               qubits[i],
                               args=(str(layer_new_params[i][1]), ))
                    for i in range(len(layer_new_params))
                ]
                theta3_layer = [
                    _lbl.Label(zrotname,
                               qubits[i],
                               args=(str(layer_new_params[i][2]), ))
                    for i in range(len(layer_new_params))
                ]

            #add to mirror circuit
            c.append_circuit_inplace(
                _cir.Circuit([theta3_layer], line_labels=circ.line_labels))
            c.append_circuit_inplace(
                _cir.Circuit([Xpi2layer], line_labels=circ.line_labels))
            c.append_circuit_inplace(
                _cir.Circuit([theta2_layer], line_labels=circ.line_labels))
            c.append_circuit_inplace(
                _cir.Circuit([Xpi2layer], line_labels=circ.line_labels))
            c.append_circuit_inplace(
                _cir.Circuit([theta1_layer], line_labels=circ.line_labels))

            d_ind += 5

        else:
            inverse_layer = [
                compute_gate_inverse(gate_label) for gate_label in layer
            ]
            c.append_circuit_inplace(
                _cir.Circuit([inverse_layer], line_labels=circ.line_labels))
            d_ind += 1

    #now that we've built the simple mirror circuit, let's add pauli frame randomization
    d_ind = 0
    mc = []
    net_paulis = {q: 0 for q in qubits}
    d = c.depth
    correction_angles = {
        q: 0
        for q in qubits
    }  # corrections used in the cz(theta) case, which do nothing otherwise.

    while d_ind < d:
        layer = c.layer(d_ind)
        if len(layer) > 0 and layer[
                0].name == zrotname:  # ask if it's a Zrot layer.
            #It's necessary for the whole layer to have Zrot gates
            #if the layer is 1Q unitaries, pauli randomize
            current_layers = c[d_ind:d_ind + 5]

            #generate random pauli
            new_paulis = {q: _np.random.randint(0, 4) for q in qubits}
            new_paulis_as_layer = [
                _lbl.Label(pauli_labels[new_paulis[q]], q) for q in qubits
            ]

            net_paulis_as_layer = [
                _lbl.Label(pauli_labels[net_paulis[q]], q) for q in qubits
            ]
            #compute new net pauli based on previous pauli
            net_pauli_numbers = _symp.find_pauli_number(
                _symp.symplectic_rep_of_clifford_circuit(
                    _cir.Circuit(new_paulis_as_layer + net_paulis_as_layer,
                                 line_labels=circ.line_labels),
                    srep_dict=srep_dict)[1])

            # THIS WAS THE (THETA) VERSIONS
            #net_paulis_as_layer = [_lbl.Label(pauli_labels[net_paulis[q]], q) for q in qubits]
            #net_pauli_numbers = _symp.find_pauli_number(_symp.symplectic_rep_of_clifford_circuit(_cir.Circuit(
            #                                        new_paulis_as_layer+net_paulis_as_layer), pspec=pspec)[1])
            net_paulis = {qubits[i]: net_pauli_numbers[i] for i in range(n)}

            #depending on what the net pauli before the U gate is, might need to change parameters on the U gate
            # to commute the pauli through
            #recompile current layer to account for this and recompile with these paulis
            if n == 1:
                old_params_and_paulis = [
                    (float(current_layers[0].args[0]),
                     float(current_layers[2].args[0]),
                     float(current_layers[4].args[0]), net_paulis[qubits[i]],
                     new_paulis[qubits[i]]) for i in range(n)
                ]
            else:
                old_params_and_paulis = [
                    (float(current_layers[0][i].args[0]),
                     float(current_layers[2][i].args[0]),
                     float(current_layers[4][i].args[0]),
                     net_paulis[qubits[i]], new_paulis[qubits[i]])
                    for i in range(n)
                ]

            layer_new_params = [
                _comp.pauli_frame_randomize_unitary(*p)
                for p in old_params_and_paulis
            ]
            #recompile any zrotation corrections from the previous Czr into the first zr of this layer. This correction
            # will be zero if there are no Czr gates (when it's clifford+zxzxz)
            theta1_layer = [
                _lbl.Label(zrotname,
                           qubits[i],
                           args=(str(layer_new_params[i][0] +
                                     correction_angles[qubits[i]]), ))
                for i in range(len(layer_new_params))
            ]
            theta2_layer = [
                _lbl.Label(zrotname,
                           qubits[i],
                           args=(str(layer_new_params[i][1]), ))
                for i in range(len(layer_new_params))
            ]
            theta3_layer = [
                _lbl.Label(zrotname,
                           qubits[i],
                           args=(str(layer_new_params[i][2]), ))
                for i in range(len(layer_new_params))
            ]

            #add to mirror circuit
            mc.append([theta1_layer])
            mc.append([Xpi2layer])
            mc.append([theta2_layer])
            mc.append([Xpi2layer])
            mc.append([theta3_layer])

            d_ind += 5
            # reset the correction angles.
            correction_angles = {q: 0 for q in qubits}

        else:
            if circ_type == 'clifford+zxzxz':
                net_paulis_as_layer = [
                    _lbl.Label(pauli_labels[net_paulis[qubits[i]]], qubits[i])
                    for i in range(n)
                ]
                circ_sandwich = _cir.Circuit(
                    [layer, net_paulis_as_layer, layer],
                    line_labels=circ.line_labels)
                net_paulis = {
                    qubits[i]: pn
                    for i, pn in enumerate(
                        _symp.find_pauli_number(
                            _symp.symplectic_rep_of_clifford_circuit(
                                circ_sandwich, srep_dict=srep_dict)[1]))
                }
                mc.append(layer)
                #we need to account for how the net pauli changes when it gets passed through the clifford layers

            if circ_type == 'cz(theta)+zxzxz':
                quasi_inv_layer = []
                #recompile layer taking into acount paulis
                for g in layer:
                    if g.name == czrotname:
                        #get the qubits, figure out net pauli on those qubits
                        gate_qubits = g.qubits
                        net_paulis_for_gate = (net_paulis[gate_qubits[0]],
                                               net_paulis[gate_qubits[1]])
                        theta = float(g.args[0])
                        if ((net_paulis_for_gate[0] % 3 != 0
                             and net_paulis_for_gate[1] % 3 == 0)
                                or (net_paulis_for_gate[0] % 3 == 0
                                    and net_paulis_for_gate[1] % 3 != 0)):
                            theta *= -1
                        quasi_inv_layer.append(
                            _lbl.Label(czrotname,
                                       gate_qubits,
                                       args=(str(theta), )))
                        #for each X or Y, do a Zrotation by -theta on the other qubit after the 2Q gate.
                        for q in gate_qubits:
                            if net_paulis[q] == 1 or net_paulis[q] == 2:
                                for q2 in gate_qubits:
                                    if q2 != q:
                                        correction_angles[q2] += -1 * theta
                    else:
                        quasi_inv_layer.append(
                            _lbl.Label(compute_gate_inverse(g)))
                    #add to circuit
                    mc.append([quasi_inv_layer])

            #increment position in circuit
            d_ind += 1

    #update the target pauli
    #pauli_layer = [_lbl.Label(pauli_labels[net_paulis[i]], qubits[i]) for i in range(len(qubits))]
    # The version from (THETA)
    pauli_layer = [_lbl.Label(pauli_labels[net_paulis[q]], q) for q in qubits]
    conjugation_circ = _cir.Circuit([pauli_layer],
                                    line_labels=circ.line_labels)
    telp_s, telp_p = _symp.symplectic_rep_of_clifford_circuit(
        conjugation_circ, srep_dict=srep_dict)

    # Calculate the bit string that this mirror circuit should output, from the final telescoped Pauli.
    target_bitstring = ''.join(['1' if p == 2 else '0' for p in telp_p[n:]])
    mirror_circuit = _cir.Circuit(mc, line_labels=circ.line_labels)

    return mirror_circuit, target_bitstring