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))
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)
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])
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])
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.)
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.)
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)
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,
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