Exemple #1
0
    def test_circuit_symplectic_representation(self):
        srep_dict = symplectic.compute_internal_gate_symplectic_representations(
        )

        # Dummy circuit where HZHX = I
        # Running on qubit 1 of two to ensure proper indexing of operations into full matrix
        HZHcirc = pygsti.circuits.Circuit([('H', 1), ('Z', 1), ('H', 1),
                                           ('X', 1)],
                                          num_lines=2)
        s, p = symplectic.symplectic_rep_of_clifford_circuit(HZHcirc)
        self.assertArraysAlmostEqual(s, np.eye(4))
        self.assertArraysAlmostEqual(p, np.zeros(4))

        # Also test with non-hardcoded names
        HZHcirc = pygsti.circuits.Circuit([('Gh', 1), ('Gzpi', 1), ('Gh', 1),
                                           ('Gxpi', 1)],
                                          num_lines=2)
        srep_custom = {
            'Gh': srep_dict['H'],
            'Gzpi': srep_dict['Z'],
            'Gxpi': srep_dict['X']
        }
        s, p = symplectic.symplectic_rep_of_clifford_circuit(
            HZHcirc, srep_dict=srep_custom)
        self.assertArraysAlmostEqual(s, np.eye(4))
        self.assertArraysAlmostEqual(p, np.zeros(4))
Exemple #2
0
 def test_compile_symplectic(self):
     compiled = compilers.compile_symplectic(fixture_2Q.clifford_sym,
                                             **self.options)
     # TODO assert intermediate correctness
     sym_out, _ = symplectic.symplectic_rep_of_clifford_circuit(
         compiled, pspec=self.options.get('pspec', None))
     self.assertArraysEqual(fixture_2Q.clifford_sym, sym_out)
Exemple #3
0
 def check_out_symplectic(c, pspec, s, p, n):
     s0, p0 = symplectic.prep_stabilizer_state(n)
     sc, pc = symplectic.symplectic_rep_of_clifford_circuit(c,pspec=pspec)
     scout, pcout = symplectic.apply_clifford_to_stabilizer_state(sc,pc,s0,p0)
     stargetout, ptargetout = symplectic.apply_clifford_to_stabilizer_state(s,p,s0,p0)
     for i in range(n):
         mtout = symplectic.pauli_z_measurement(stargetout,ptargetout,i)
         mcout = symplectic.pauli_z_measurement(scout,pcout,i)
         self.assertArraysAlmostEqual(mtout[0],mcout[0])
Exemple #4
0
 def test_compile_stabilizer_state(self):
     compiled = compilers.compile_stabilizer_state(
         self.fixture.clifford_sym, self.fixture.clifford_phase,
         **self.options)
     sym_0, phase_0 = symplectic.prep_stabilizer_state(self.fixture.n)
     sym_compiled, phase_compiled = symplectic.symplectic_rep_of_clifford_circuit(
         compiled, pspec=self.options.get('pspec', None))
     sym_out, phase_out = symplectic.apply_clifford_to_stabilizer_state(
         sym_compiled, phase_compiled, sym_0, phase_0)
     sym_target, phase_target = symplectic.apply_clifford_to_stabilizer_state(
         self.fixture.clifford_sym, self.fixture.clifford_phase, sym_0,
         phase_0)
     for i in range(self.fixture.n):
         self.assertArraysAlmostEqual(
             symplectic.pauli_z_measurement(sym_target, phase_target, i)[0],
             symplectic.pauli_z_measurement(sym_out, phase_out, i)[0])
Exemple #5
0
    def test_compile_stabilizer_measurement(self):
        compiled = compilers.compile_stabilizer_measurement(
            self.fixture.clifford_sym, self.fixture.clifford_phase,
            **self.options)
        sym_compiled, phase_compiled = symplectic.symplectic_rep_of_clifford_circuit(
            compiled, pspec=self.options.get('pspec', None))
        sym_state, phase_state = symplectic.prep_stabilizer_state(
            self.fixture.n)
        sym_state, phase_state = symplectic.apply_clifford_to_stabilizer_state(
            self.fixture.clifford_sym, self.fixture.clifford_phase, sym_state,
            phase_state)
        sym_out, phase_out = symplectic.apply_clifford_to_stabilizer_state(
            sym_compiled, phase_compiled, sym_state, phase_state)

        # This asserts that a particular stabilizer propagation yields the expected result -
        #  the all-0 state.  This test preparation, acting-on, and measurment of stabilizer states.
        for i in range(self.fixture.n):
            self.assertAlmostEqual(
                symplectic.pauli_z_measurement(sym_out, phase_out, i)[1], 0.)
Exemple #6
0
    def test_compilers(self):

        n = 10
        # Pick a random Clifford to compile
        s, p = symplectic.random_clifford(n)
        # Directly test the core algorithm
        c = compilers.compile_symplectic_using_GGE_core(s)
        sout, pout = symplectic.symplectic_rep_of_clifford_circuit(c)
        self.assertArraysEqual(s, sout)
        # Test accessing all the allowed algorithms, without a pspec or a subsetQs
        c = compilers.compile_symplectic(s,
                                         iterations=3,
                                         algorithms=['BGGE', 'ROGGE'])

        # Tests init a pspec with limited availability, and user-specified labels.
        n = 5
        qubit_labels = ['Q' + str(i) for i in range(n)]
        availability = {
            'Gcnot':
            [('Q' + str(i), 'Q' + str(i + 1)) for i in range(0, n - 1)]
        }
        gate_names = ['Gh', 'Gp', 'Gxpi', 'Gpdag', 'Gcnot']  # 'Gi',
        pspec = ProcessorSpec(n,
                              gate_names=gate_names,
                              availability=availability,
                              qubit_labels=qubit_labels)
        s, p = symplectic.random_clifford(n)
        # Test accessing all the allowed algorithms, with a pspec but no subsetQs
        c = compilers.compile_symplectic(s,
                                         pspec=pspec,
                                         iterations=3,
                                         algorithms=['BGGE', 'ROGGE'])

        # Test accessing all the allowed algorithms, with a pspec and a subsetQs
        n = 2
        s, p = symplectic.random_clifford(n)
        c = compilers.compile_symplectic(
            s,
            pspec=pspec,
            subsetQs=['Q2', 'Q3'],
            iterations=2,
            algorithms=['BGGE', 'ROGGE', 'iAGvGE'])
        sout, pout = symplectic.symplectic_rep_of_clifford_circuit(c,
                                                                   pspec=pspec)
        self.assertArraysEqual(s, sout)

        # Test the main function that we'll access -- compile_clifford
        n = 5
        s, p = symplectic.random_clifford(n)
        c = compilers.compile_clifford(s,
                                       p,
                                       pspec=pspec,
                                       subsetQs=None,
                                       iterations=2,
                                       algorithm='ROGGE',
                                       prefixpaulis=True,
                                       paulirandomize=True)
        sout, pout = symplectic.symplectic_rep_of_clifford_circuit(c,
                                                                   pspec=pspec)
        self.assertArraysEqual(s, sout)
        c = compilers.compile_clifford(s,
                                       p,
                                       pspec=None,
                                       subsetQs=None,
                                       iterations=2,
                                       algorithm='ROGGE')
        sout, pout = symplectic.symplectic_rep_of_clifford_circuit(c,
                                                                   pspec=pspec)
        self.assertArraysEqual(s, sout)

        n = 2
        s, p = symplectic.random_clifford(n)
        c = compilers.compile_clifford(s,
                                       p,
                                       pspec=pspec,
                                       subsetQs=['Q2', 'Q3'],
                                       iterations=2,
                                       algorithm='ROGGE',
                                       prefixpaulis=True,
                                       paulirandomize=True)
        sout, pout = symplectic.symplectic_rep_of_clifford_circuit(c,
                                                                   pspec=pspec)
        self.assertArraysEqual(s, sout)
        c = compilers.compile_clifford(s,
                                       p,
                                       pspec=pspec,
                                       subsetQs=['Q2', 'Q3'],
                                       iterations=2,
                                       algorithm='BGGE',
                                       prefixpaulis=True,
                                       paulirandomize=True)
        sout, pout = symplectic.symplectic_rep_of_clifford_circuit(c,
                                                                   pspec=pspec)
        self.assertArraysEqual(s, sout)
        c = compilers.compile_clifford(s,
                                       p,
                                       pspec=pspec,
                                       subsetQs=['Q2', 'Q3'],
                                       iterations=2,
                                       algorithm='iAGvGE',
                                       prefixpaulis=True,
                                       paulirandomize=False)
        sout, pout = symplectic.symplectic_rep_of_clifford_circuit(c,
                                                                   pspec=pspec)
        self.assertArraysEqual(s, sout)

        # Check it works for the 1-qubit case.
        n = 1
        # Pick a random Clifford to compile
        s, p = symplectic.random_clifford(1)
        c = compilers.compile_clifford(s, p)
        sout, pout = symplectic.symplectic_rep_of_clifford_circuit(c)
        c = compilers.compile_clifford(s,
                                       p,
                                       pspec=pspec,
                                       subsetQs=['Q3'],
                                       iterations=2,
                                       algorithm='ROGGE',
                                       prefixpaulis=False,
                                       paulirandomize=True)
        sout, pout = symplectic.symplectic_rep_of_clifford_circuit(c,
                                                                   pspec=pspec)
        self.assertArraysEqual(s, sout)

        # Tests all CNOT compiler algorithms
        n = 8
        qubit_labels = ['Q' + str(i) for i in range(n)]
        availability = {
            'Gcnot':
            [('Q' + str(i), 'Q' + str(i + 1)) for i in range(0, n - 1)] + [
                ('Q0', 'Q2'),
            ]
        }
        gate_names = ['Gh', 'Gp', 'Gxpi', 'Gpdag', 'Gcnot']  # 'Gi',
        pspec8 = ProcessorSpec(n,
                               gate_names=gate_names,
                               availability=availability,
                               qubit_labels=qubit_labels)
        n = 6
        qubit_labels = ['Q' + str(i) for i in range(n)]
        availability = {
            'Gcphase':
            [('Q' + str(i), 'Q' + str(i + 1))
             for i in range(0, n - 1)] + [('Q' + str(n - 1), 'Q' + str(0))]
        }
        gate_names = ['Gh', 'Gxpi2', 'Gp', 'Gcphase']  # 'Gi',
        pspec6 = ProcessorSpec(n,
                               gate_names=gate_names,
                               availability=availability,
                               qubit_labels=qubit_labels)

        nsubset = 6
        circuit = []
        for i in range(100):
            a = np.random.randint(nsubset)
            b = np.random.randint(nsubset)
            if a != b:
                circuit.append(Label('CNOT', ('Q' + str(a), 'Q' + str(b))))

                subsetQs = ['Q' + str(i) for i in range(nsubset)]
        circuit = Circuit(layer_labels=circuit, line_labels=subsetQs)
        s, p = pygsti.tools.symplectic.symplectic_rep_of_clifford_circuit(
            circuit)

        aargs = {}
        aargs['COCAGE'] = []
        aargs['COiCAGE'] = []
        aargs['OCAGE'] = [
            ['Q1', 'Q0', 'Q2', 'Q5', 'Q3', 'Q4'],
        ]
        # This ordering must be a 'contraction' of the graph, with the remaining graph always connected.
        aargs['OiCAGE'] = [
            ['Q0', 'Q1', 'Q2', 'Q5', 'Q3', 'Q4'],
        ]
        aargs['ROCAGE'] = []
        for algorithm in ['COiCAGE', 'OiCAGE', 'COCAGE', 'ROCAGE']:
            c = compilers.compile_cnot_circuit(s,
                                               pspec6,
                                               algorithm=algorithm,
                                               subsetQs=None,
                                               aargs=aargs[algorithm])
            c = compilers.compile_cnot_circuit(s,
                                               pspec8,
                                               algorithm=algorithm,
                                               subsetQs=subsetQs,
                                               aargs=aargs[algorithm])

        # Tests stabilizer state and measurement functions.

        # Tests the stabilizer compilers for n = 1
        n = 1
        pspec1 = ProcessorSpec(
            nQubits=n,
            gate_names=['Gcnot', 'Gh', 'Gp', 'Gxpi', 'Gypi', 'Gzpi'])  # 'Gi',
        s, p = symplectic.random_clifford(n)
        c = compilers.compile_stabilizer_state(s,
                                               p,
                                               pspec1,
                                               algorithm='COCAGE',
                                               paulirandomize=False)
        c = compilers.compile_stabilizer_measurement(s,
                                                     p,
                                                     pspec1,
                                                     algorithm='ROCAGE',
                                                     paulirandomize=True)
        c = compilers.compile_stabilizer_measurement(s,
                                                     p,
                                                     pspec6,
                                                     subsetQs=[
                                                         'Q3',
                                                     ],
                                                     algorithm='COiCAGE',
                                                     paulirandomize=False)

        def check_out_symplectic(c, pspec, s, p, n):
            s0, p0 = symplectic.prep_stabilizer_state(n)
            sc, pc = symplectic.symplectic_rep_of_clifford_circuit(c,
                                                                   pspec=pspec)
            scout, pcout = symplectic.apply_clifford_to_stabilizer_state(
                sc, pc, s0, p0)
            stargetout, ptargetout = symplectic.apply_clifford_to_stabilizer_state(
                s, p, s0, p0)
            for i in range(n):
                mtout = symplectic.pauli_z_measurement(stargetout, ptargetout,
                                                       i)
                mcout = symplectic.pauli_z_measurement(scout, pcout, i)
                self.assertArraysAlmostEqual(mtout[0], mcout[0])

        n = 6
        s, p = symplectic.random_clifford(n)
        c = compilers.compile_stabilizer_state(s,
                                               p,
                                               pspec6,
                                               algorithm='ROCAGE',
                                               paulirandomize=False)
        check_out_symplectic(c, pspec6, s, p, n)

        s, p = symplectic.random_clifford(n)
        c = compilers.compile_stabilizer_state(s,
                                               p,
                                               pspec6,
                                               algorithm='COiCAGE',
                                               paulirandomize=True)
        check_out_symplectic(c, pspec6, s, p, n)

        s, p = symplectic.random_clifford(3)
        c = compilers.compile_stabilizer_measurement(
            s,
            p,
            pspec6,
            subsetQs=['Q3', 'Q4', 'Q5'],
            algorithm='COiCAGE',
            paulirandomize=False)
        sc, pc = symplectic.symplectic_rep_of_clifford_circuit(c, pspec=pspec6)
        # The state the c should map to |0,0,0,....>.
        sstate, pstate = symplectic.prep_stabilizer_state(3)
        sstate, pstate = symplectic.apply_clifford_to_stabilizer_state(
            s, p, sstate, pstate)
        sout, pout = symplectic.apply_clifford_to_stabilizer_state(
            sc, pc, sstate, pstate)
        for i in range(3):
            mtout = symplectic.pauli_z_measurement(sout, pout, i)
            self.assertArraysAlmostEqual(mtout[1], 0.)

        s, p = symplectic.random_clifford(n)
        c1 = compilers.compile_stabilizer_state(s,
                                                p,
                                                pspec6,
                                                algorithm='COiCAGE',
                                                paulirandomize=False)
        c2 = compilers.compile_stabilizer_measurement(s,
                                                      p,
                                                      pspec6,
                                                      algorithm='COiCAGE',
                                                      paulirandomize=True)
        c2 = c2.copy(editable=True)
        c2.prefix_circuit(c1)
        zerosstate_s, zerosstate_p = symplectic.prep_stabilizer_state(n)
        sc, pc = symplectic.symplectic_rep_of_clifford_circuit(c2,
                                                               pspec=pspec6)
        scout, pcout = symplectic.apply_clifford_to_stabilizer_state(
            sc, pc, zerosstate_s, zerosstate_p)
        for i in range(n):
            mtout = symplectic.pauli_z_measurement(scout, pcout, i)
            self.assertArraysAlmostEqual(mtout[1], 0.)
Exemple #7
0
 def test_compile_symplectic_using_GGE_core(self):
     compiled = compilers._compile_symplectic_using_gge_core(
         fixture_2Q.clifford_sym)
     # TODO assert intermediate correctness
     sym_out, _ = symplectic.symplectic_rep_of_clifford_circuit(compiled)
     self.assertArraysEqual(fixture_2Q.clifford_sym, sym_out)
Exemple #8
0
fixture_2Q.clifford_peq = CliffordCompilationRules.create_standard(
    fixture_2Q.pspec,
    compile_type="paulieq",
    what_to_compile=("1Qcliffords", "allcnots"),
    verbosity=1)

# Totally arbitrary CNOT circuit
fixture_2Q.cnot_circuit = Circuit(layer_labels=[
    Label('CNOT', ('Q1', 'Q0')),
    Label('CNOT', ('Q1', 'Q0')),
    Label('CNOT', ('Q0', 'Q1')),
    Label('CNOT', ('Q1', 'Q0'))
],
                                  line_labels=fixture_2Q.qubit_labels)
fixture_2Q.cnot_circuit_sym, fixture_2Q.cnot_circuit_phase = \
    symplectic.symplectic_rep_of_clifford_circuit(fixture_2Q.cnot_circuit)

fixture_3Q = Namespace(
    n=3,
    qubit_labels=['Q0', 'Q1', 'Q2'],
    availability={'Gcnot': [('Q0', 'Q1'), ('Q1', 'Q2')]},
    gate_names=['Gh', 'Gp', 'Gxpi', 'Gpdag', 'Gcnot'],
    # generated as before:
    clifford_sym=np.array([[0, 0, 1, 1, 0, 1], [0, 0, 0, 0, 1, 0],
                           [1, 0, 0, 0, 1, 1], [1, 0, 1, 1, 1, 1],
                           [0, 1, 0, 1, 1, 0], [0, 0, 1, 0, 0, 1]]),
    clifford_phase=np.array([2, 2, 3, 1, 1, 2]))
fixture_3Q.pspec = QubitProcessorSpec(fixture_3Q.n,
                                      gate_names=fixture_3Q.gate_names,
                                      availability=fixture_3Q.availability,
                                      qubit_labels=fixture_3Q.qubit_labels,
Exemple #9
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