Skip to content

Commit

Permalink
Rough fix of intermediate measurements
Browse files Browse the repository at this point in the history
Added 'Iz' intermediate measurement to openqasm conversion and instituted the necessary steps to run on IBMQ. Parity check intermediate measurement is in development and is currently very rough. Miscellaneous updates to IBMQ monitor also added.
  • Loading branch information
pcwysoc committed Sep 15, 2023
1 parent 843d714 commit 99525b6
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 21 deletions.
37 changes: 27 additions & 10 deletions pygsti/circuits/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down
39 changes: 29 additions & 10 deletions pygsti/extras/ibmq/ibmqcore.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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'])):
Expand All @@ -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):
Expand Down
2 changes: 1 addition & 1 deletion pygsti/tools/internalgates.py
Original file line number Diff line number Diff line change
Expand Up @@ -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']
Expand Down

0 comments on commit 99525b6

Please sign in to comment.