Skip to content

Commit 515a7e8

Browse files
committed
Add from_qiskit method to Circuit class
1 parent 57a7e67 commit 515a7e8

File tree

3 files changed

+187
-93
lines changed

3 files changed

+187
-93
lines changed

pygsti/circuits/circuit.py

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3974,6 +3974,159 @@ def from_cirq(cls, circuit, qubit_conversion=None, cirq_gate_conversion= None,
39743974
else:
39753975
return cls(circuit_layers)
39763976

3977+
@classmethod
3978+
def from_qiskit(cls, circuit, qubit_conversion=None, qiskit_gate_conversion=None, use_standard_gate_conversion_as_backup=True, allow_different_gates_in_same_layer=True):
3979+
"""
3980+
Converts and instantiates a pyGSTi Circuit object from a Qiskit QuantumCircuit object.
3981+
3982+
Parameters
3983+
----------
3984+
circuit : Qiskit QuantumCircuit
3985+
The Qiskit QuantumCircuit object to parse into a pyGSTi circuit.
3986+
3987+
qubit_conversion : dict, optional (default None)
3988+
A dictionary specifying a mapping between Qiskit qubit indices and
3989+
pyGSTi qubit labels (either integers or strings).
3990+
If None, then a default mapping is created.
3991+
3992+
qiskit_gate_conversion : dict, optional (default None)
3993+
If specified a dictionary with keys given by Qiskit gate objects,
3994+
and values given by pyGSTi gate names which overrides the built-in
3995+
conversion dictionary used by default.
3996+
3997+
use_standard_gate_conversion_as_backup : boolean (default True)
3998+
Determines how the circuit conversion will be handled when the custom
3999+
Qiskit gate conversion dict does not have an entry for the encountered
4000+
gate. If True, this method will fall back on the standard conversions
4001+
found in qiskit_gatenames_standard_conversions(). If False, the method
4002+
will fail.
4003+
4004+
allow_different_gates_in_same_layer: boolean (default True)
4005+
determines if gates with different names can be in the same layer.
4006+
For instance, if a CZ gate and CX gate can both fit in the same layer,
4007+
they will either be placed in the same layer (if True) or split into
4008+
separate layers (if False).
4009+
4010+
Returns
4011+
-------
4012+
pygsti_circuit
4013+
A pyGSTi Circuit instance equivalent to the specified Qiskit one.
4014+
"""
4015+
4016+
try:
4017+
import qiskit
4018+
except ImportError:
4019+
raise ImportError("Qiskit is required for this operation, and it does not appear to be installed.")
4020+
4021+
#mapping between cirq gates and pygsti gate names:
4022+
if qiskit_gate_conversion is not None:
4023+
if use_standard_gate_conversion_as_backup == True:
4024+
qiskit_to_gate_name_mapping = _itgs.qiskit_gatenames_standard_conversions()
4025+
for key, value in qiskit_gate_conversion.items():
4026+
qiskit_to_gate_name_mapping[key] = value
4027+
else:
4028+
qiskit_to_gate_name_mapping = qiskit_gate_conversion
4029+
else:
4030+
qiskit_to_gate_name_mapping = _itgs.qiskit_gatenames_standard_conversions()
4031+
4032+
#get all of the qubits in the Qiskit circuit
4033+
qubits = circuit.qubits
4034+
qiskit_qubit_indices = []
4035+
for qubit in qubits:
4036+
qiskit_qubit_indices.append(qubit._index)
4037+
4038+
4039+
#ensure all of these have a conversion available.
4040+
if qubit_conversion is not None:
4041+
assert set(qiskit_qubit_indices).issubset(set(qubit_conversion.keys())), 'Missing Qiskit to pygsti conversions for some qubit label(s).'
4042+
#if it is None, build a default mapping.
4043+
else:
4044+
#default mapping is the identify mapping: qubit i in the Qiskit circuit maps to qubit i in the pyGSTi circuit
4045+
qubit_conversion = {i: f'Q{i}' for i in qiskit_qubit_indices}
4046+
4047+
line_labels = tuple(sorted([qubit_conversion[qubit] for qubit in qiskit_qubit_indices]))
4048+
4049+
# print(qubit_conversion)
4050+
4051+
# print(line_labels)
4052+
4053+
4054+
layer_indices = {}
4055+
for line_label in line_labels:
4056+
layer_indices[line_label] = 0
4057+
4058+
instructions = circuit.data
4059+
4060+
pygsti_circ_layers = []
4061+
4062+
if allow_different_gates_in_same_layer == False:
4063+
layer_names = []
4064+
4065+
for instruction in instructions:
4066+
# print(layer_indices)
4067+
if allow_different_gates_in_same_layer == False:
4068+
assert len(pygsti_circ_layers) == len(layer_names), "there must be one layer name for each layer!"
4069+
4070+
# print(instruction)
4071+
name = instruction.operation.name
4072+
num_qubits = instruction.operation.num_qubits
4073+
instruction_qubit_indices = [qubit._index for qubit in instruction.qubits]
4074+
params = instruction.operation.params
4075+
# print(name)
4076+
# print(num_qubits)
4077+
# print(params)
4078+
4079+
pygsti_qubits = [qubit_conversion[i] for i in instruction_qubit_indices]
4080+
4081+
if name == 'measure' or name == 'barrier':
4082+
_warnings.warn('skipping measure or barrier')
4083+
continue
4084+
4085+
pygsti_gate_name = qiskit_to_gate_name_mapping[name][0]
4086+
4087+
label = _Label(pygsti_gate_name, pygsti_qubits, args=params)
4088+
4089+
4090+
#convert_qiskit_instruction_to_pygsti_label(name, qubits, params)
4091+
4092+
next_index = max(layer_indices[qubit] for qubit in pygsti_qubits)
4093+
# print(f'computed next index for {pygsti_gate_name} gate on lines {pygsti_qubits}: {next_index}')
4094+
4095+
if allow_different_gates_in_same_layer == True: #layers are not separated by the type of gate they contain
4096+
4097+
if next_index < len(pygsti_circ_layers): #there is an existing layer in the circuit where the gate can be inserted
4098+
pygsti_circ_layers[next_index].append(label)
4099+
# print(f"inserting {pygsti_gate_name} gate in layer {next_index} on lines {pygsti_qubits}")
4100+
for pygsti_qubit in pygsti_qubits: #update where a gate on these qubits can be placed
4101+
layer_indices[pygsti_qubit] = next_index + 1
4102+
else:
4103+
# print(f"inserting {pygsti_gate_name} gate at end of circuit, which is layer {len(pygsti_circ_layers)} on lines {pygsti_qubits}")
4104+
pygsti_circ_layers.append([label]) #need to append gate at the end of the circuit, thus creating a new layer
4105+
for pygsti_qubit in pygsti_qubits:
4106+
layer_indices[pygsti_qubit] = len(pygsti_circ_layers)
4107+
4108+
else:
4109+
for i in range(next_index, len(pygsti_circ_layers)): # searching for a layer where the gate can be inserted. Layer name needs to match the name of the gate being inserted
4110+
if name == layer_names[i]:
4111+
# print(f'inserting gate {name} on qubits {qubits} in layer {i}')
4112+
pygsti_circ_layers[i].append(label)
4113+
for pygsti_qubit in pygsti_qubits:
4114+
layer_indices[pygsti_qubit] = i + 1
4115+
break
4116+
4117+
else: #no place to put the gate in the existing layers. New layer is added with corresponding name
4118+
# print(f'inserting gate {name} on qubits {qubits} in layer {len(pygsti_circ_layers)}')
4119+
pygsti_circ_layers.append([label])
4120+
layer_names.append(name)
4121+
for pygsti_qubit in pygsti_qubits:
4122+
layer_indices[pygsti_qubit] = len(pygsti_circ_layers)
4123+
4124+
circuit = cls(pygsti_circ_layers, line_labels=line_labels)
4125+
4126+
return circuit
4127+
4128+
4129+
39774130
def convert_to_quil(self,
39784131
num_qubits=None,
39794132
gatename_conversion=None,

pygsti/processors/qiskit_to_pygsti.py

Lines changed: 0 additions & 93 deletions
This file was deleted.

pygsti/tools/internalgates.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,40 @@ def cirq_gatenames_standard_conversions():
501501
return cirq_to_standard_mapping
502502

503503

504+
def qiskit_gatenames_standard_conversions():
505+
506+
"""
507+
A dictionary converting Qiskit gates (based on Instruction.name) to built-in pyGSTi names for these gates. Additional flag in dict value indicates if the gate has params
508+
Does not currently support conversion of all cirq gate types.
509+
"""
510+
511+
try:
512+
import qiskit
513+
except ImportError:
514+
raise ImportError("Qiskit is required for this operation, and it does not appear to be installed.")
515+
516+
qiskit_to_standard_mapping = {}
517+
518+
qiskit_to_standard_mapping['id'] = ['Gi', False]
519+
qiskit_to_standard_mapping['x'] = ['Gxpi', False]
520+
qiskit_to_standard_mapping['y'] = ['Gypi', False]
521+
qiskit_to_standard_mapping['z'] = ['Gzpi', False]
522+
qiskit_to_standard_mapping['sx'] = ['Gxpi2', False]
523+
qiskit_to_standard_mapping['t'] = ['Gt', False]
524+
qiskit_to_standard_mapping['h'] = ['Gh', False]
525+
526+
qiskit_to_standard_mapping['rz'] = ['Gzr', True]
527+
qiskit_to_standard_mapping['u'] = ['Gu3', True]
528+
529+
qiskit_to_standard_mapping['cx'] = ['Gcnot', False]
530+
qiskit_to_standard_mapping['cz'] = ['Gcphase', False]
531+
qiskit_to_standard_mapping['ecr'] = ['Gecres', False]
532+
qiskit_to_standard_mapping['swap'] = ['Gswap', False]
533+
534+
535+
return qiskit_to_standard_mapping
536+
537+
504538
def standard_gatenames_quil_conversions():
505539
"""
506540
A dictionary converting the gates with standard names to the QUIL names for these gates.

0 commit comments

Comments
 (0)