diff --git a/pygsti/circuits/circuit.py b/pygsti/circuits/circuit.py index b8867e33d..a79d1b96c 100644 --- a/pygsti/circuits/circuit.py +++ b/pygsti/circuits/circuit.py @@ -3872,7 +3872,7 @@ def convert_to_openqasm(self, num_qubits=None, gatename_conversion=None, qubit_conversion=None, block_between_layers=True, block_between_gates=False, - gateargs_map=None): # TODO + gateargs_map=None, ancilla_label=None): # TODO """ Converts this circuit to an openqasm string. @@ -3917,6 +3917,11 @@ def convert_to_openqasm(self, num_qubits=None, this dictionary need not be specified, and an automatic conversion to the standard openqasm format will be implemented. + ancilla_label : string or int, optional + If not None, a string indicating the qubit label of the ancilla used for mid-circuit + measurement. If a string, should be of the form 'Qi' for integer i. Currently does + not support multi-ancillae mid-circuit measurement. + Returns ------- str @@ -3934,24 +3939,29 @@ def convert_to_openqasm(self, num_qubits=None, if all([q[0] == 'Q' for q in self.line_labels]): standardtype = True qubit_conversion = {llabel: int(llabel[1:]) for llabel in self.line_labels} + if ancilla_label != None: + qubit_conversion[ancilla_label] = int(ancilla_label[1:]) if all([isinstance(q, int) for q in self.line_labels]): qubit_conversion = {q: q for q in self.line_labels} standardtype = True if not standardtype: raise ValueError( "No standard qubit labelling conversion is available! Please provide `qubit_conversion`.") - + #Make sure that 'Qi' and i both work! if num_qubits is None: num_qubits = len(self.line_labels) + if ancilla_label != None: + num_qubits += 1 # if gateargs_map is None: # gateargs_map = {} #Currently only using 'Iz' as valid intermediate measurement ('IM') label. #Todo: Expand to all intermediate measurements. - if 'Iz' in self.str: + if 'Iz' or 'Ipc' in self.str: # using_IMs = True - num_IMs = self.str.count('Iz') + num_IMs = self.expand_subcircuits().str.count('Iz') + num_IMs += self.expand_subcircuits().str.count('Ipc') else: # using_IMs = False num_IMs = 0 @@ -3981,7 +3991,7 @@ def convert_to_openqasm(self, num_qubits=None, assert(len(gate_qubits) <= 2), 'Gates on more than 2 qubits given; this is currently not supported!' # Find the openqasm for the gate. - if gate.name.__str__() != 'Iz': + if gate.name.__str__() != 'Iz' and gate.name.__str__() != 'Ipc': openqasmlist_for_gate = gatename_conversion.get(gate.name, None) if openqasmlist_for_gate is None: @@ -4018,11 +4028,18 @@ def convert_to_openqasm(self, num_qubits=None, openqasm_for_gate += 'q[{0}];\n'.format(str(qubit_conversion[self.line_labels[-1]])) else: - assert len(gate.qubits) == 1 - q = gate.qubits[0] - # classical_bit = num_IMs_used - openqasm_for_gate = "measure q[{0}] -> cr[{1}];\n".format(str(qubit_conversion[q]), num_IMs_used) - num_IMs_used += 1 + assert gate.name.__str__() == 'Iz' or 'Ipc' + num_Ims_used = 0 + if gate.name.__str__() == 'Iz': + q = gate.qubits[0] + # classical_bit = num_IMs_used + openqasm_for_gate = "measure q[{0}] -> cr[{1}];\n".format(str(qubit_conversion[q]), num_IMs_used) + else: + openqasm_for_gate = "" + for control in gate_qubits: + openqasm_for_gate += "cx q[{0}], q[{1}];\n".format(str(qubit_conversion[control]), qubit_conversion[ancilla_label]) + openqasm_for_gate += "measure q[{0}] -> cr[{1}];\n".format(qubit_conversion[ancilla_label], num_IMs_used) + num_Ims_used += 1 # Add the openqasm for the gate to the openqasm string. openqasm += openqasm_for_gate diff --git a/pygsti/extras/ibmq/ibmqcore.py b/pygsti/extras/ibmq/ibmqcore.py index 4a783d01a..04f3206b5 100644 --- a/pygsti/extras/ibmq/ibmqcore.py +++ b/pygsti/extras/ibmq/ibmqcore.py @@ -57,6 +57,22 @@ def partial_trace(ordered_target_indices, input_dict): output_dict[new_string] = input_dict[bitstring] return output_dict +def to_labeled_counts(input_dict, ordered_target_indices, num_qubits_in_pspec): + outcome_labels = [] + counts_data = [] + for bitstring, count in input_dict.items(): + new_label = [] + term_string = '' + term_bits = bitstring[:num_qubits_in_pspec][::-1] + mid_bits = bitstring[num_qubits_in_pspec:][::-1] + for index in ordered_target_indices: + term_string += term_bits[index] + for bit in mid_bits: + new_label.append('p'+bit) + new_label.append(term_string) + outcome_labels.append(tuple(new_label)) + counts_data.append(count) + return outcome_labels, counts_data def q_list_to_ordered_target_indices(q_list, num_qubits): if q_list is None: @@ -71,7 +87,7 @@ def q_list_to_ordered_target_indices(q_list, num_qubits): class IBMQExperiment(dict): - def __init__(self, edesign, pspec, remove_duplicates=True, randomized_order=True, circuits_per_batch=75, + def __init__(self, edesign, pspec, ancilla_label=None,remove_duplicates=True, randomized_order=True, circuits_per_batch=75, num_shots=1024): """ A object that converts pyGSTi ExperimentDesigns into jobs to be submitted to IBM Q, submits these @@ -162,7 +178,7 @@ def __init__(self, edesign, pspec, remove_duplicates=True, randomized_order=True #openqasm_circuit_ids = [] for circ_idx, circ in enumerate(circuit_batch): pygsti_openqasm_circ = circ.convert_to_openqasm(num_qubits=pspec.num_qubits, - standard_gates_version='x-sx-rz') + standard_gates_version='x-sx-rz', ancilla_label=ancilla_label) qiskit_qc = _qiskit.QuantumCircuit.from_qasm_str(pygsti_openqasm_circ) self['pygsti_openqasm_circuits'][batch_idx].append(pygsti_openqasm_circ) @@ -321,7 +337,7 @@ def monitor(self): status = qjob.status() print("Batch {}: {}".format(counter + 1, status)) if status.name == 'QUEUED': - print(' - Queue position is {}'.format(qjob.queue_position())) + print(' - Queue position is {}'.format(qjob.queue_position(refresh=True))) #maybe refresh here? Could also make this whole thing autorefresh? Overall job monitoring could be more sophisticated # Print unsubmitted for any entries in qobj but not qjob for counter in range(len(self['qjob']), len(self['qobj'])): @@ -339,16 +355,19 @@ def retrieve_results(self): #get results from backend jobs and add to dict ds = _data.DataSet() for exp_idx, qjob in enumerate(self['qjob']): - print("Querying IBMQ for results objects for batch {}...".format(exp_idx)) - batch_result = qjob.result() + print("Querying IBMQ for results objects for batch {}...".format(exp_idx+1)) #+ 1 here? + batch_result = qjob.result() + #results of qjob with associated exp_idx self['batch_result_object'].append(batch_result) #exp_dict['batch_data'] = [] - for i, circ in enumerate(self['pygsti_circuits'][exp_idx]): + num_qubits_in_pspec = self['pspec'].num_qubits + for i, circ in enumerate(self['pygsti_circuits'][exp_idx]): ordered_target_indices = [self['pspec'].qubit_labels.index(q) for q in circ.line_labels] - counts_data = partial_trace(ordered_target_indices, reverse_dict_key_bits(batch_result.get_counts(i))) - #exp_dict['batch_data'].append(counts_data) - ds.add_count_dict(circ, counts_data) - + #assumes qubit labeling of the form 'Q0' not '0' + labeled_counts = to_labeled_counts(batch_result.get_counts(i), ordered_target_indices, num_qubits_in_pspec) + outcome_labels = labeled_counts[0] + counts_data = labeled_counts[1] + ds.add_count_list(circ, outcome_labels, counts_data) self['data'] = _ProtocolData(self['edesign'], ds) def write(self, dirname=None): diff --git a/pygsti/tools/internalgates.py b/pygsti/tools/internalgates.py index e3664f79c..2ed3ab87c 100644 --- a/pygsti/tools/internalgates.py +++ b/pygsti/tools/internalgates.py @@ -551,7 +551,7 @@ def standard_gatenames_openqasm_conversions(version='u3'): """ if version == 'u3': std_gatenames_to_qasm = {} - std_gatenames_to_qasm['Gi'] = ['id'] + std_gatenames_to_qasm['Gi'] = ['id'] #delay? std_gatenames_to_qasm['Gxpi2'] = ['u3(1.570796326794897, 4.71238898038469, 1.570796326794897)'] # [1,3,1]*pi/2 std_gatenames_to_qasm['Gxmpi2'] = ['u3(1.570796326794897, 1.570796326794897, 4.71238898038469)'] # [1,1,3]*pi/2 std_gatenames_to_qasm['Gxpi'] = ['x']