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))))
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)
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)
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)
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
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)
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'))
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)
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)
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)
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, ((),))
def test_empty_tuple_makes_idle_layer(self): c = circuit.Circuit(['Gi', Label(())]) self.assertEqual(len(c), 2)
def __delitem__(self, circuit): if not isinstance(circuit, _cir.Circuit): circuit = _cir.Circuit(circuit) del self._info[self.cirIndex[circuit]] del self.cirIndex[circuit]
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)
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'))
def setUp(self): self.s1 = circuit.Circuit(('Gx', 'Gx'), stringrep="Gx^2") self.s2 = circuit.Circuit(self.s1, stringrep="Gx^2")
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))
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)
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