-
Notifications
You must be signed in to change notification settings - Fork 31
/
Copy pathtranslate_openqasm.py
130 lines (105 loc) · 5.45 KB
/
translate_openqasm.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# Copyright SandboxAQ 2021-2024.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Functions helping with quantum circuit format conversion between abstract
format and a subset of the openqasm format (the format generated by IBM openqasm
functionalities in qiskit).
In order to produce an equivalent circuit for the target backend, it is
necessary to account for:
- how the gate names differ between the source backend to the target backend.
- how the order and conventions for some of the inputs to the gate operations
may also differ.
"""
import re
from math import pi
from tangelo.linq import Gate, Circuit
def get_openqasm_gates():
"""Map gate name of the abstract format to the equivalent gate name used in
OpenQASM. OpenQASM is a general format that allows users to express a quantum
program, define conditional operations manipulating quantum and qubit
registers, as well as defining new quantum unitaries. We however make the
choice here to support well-known gate operations.
"""
GATE_OPENQASM = dict()
for name in {"H", "X", "Y", "Z", "S", "T", "RX", "RY", "RZ", "MEASURE",
"CZ", "CY", "CRZ", "SWAP", "CSWAP"}:
GATE_OPENQASM[name] = name.lower()
GATE_OPENQASM["CNOT"] = "cx"
GATE_OPENQASM["PHASE"] = "p"
GATE_OPENQASM["CPHASE"] = "cp"
return GATE_OPENQASM
def translate_c_to_openqasm(source_circuit):
"""Take in an abstract circuit, return a OpenQASM 2.0 string using IBM
Qiskit (they are the reference for OpenQASM).
Args:
source_circuit: quantum circuit in the abstract format.
Returns:
str: the corresponding OpenQASM program, as per IBM Qiskit.
"""
from .translate_qiskit import translate_c_to_qiskit
from qiskit.qasm2 import dumps
return dumps(translate_c_to_qiskit(source_circuit))
def translate_c_from_openqasm(openqasm_str):
"""Take an OpenQASM 2.0 string as input (as defined by IBM Qiskit), return
the equivalent abstract circuit. Only a subset of OpenQASM supported, mostly
to be able to go back and forth QASM and abstract representations to
leverage tools and innovation implemented to work in the QASM format. Not
designed to support elaborate QASM programs defining their own operations.
Compatible with qiskit.QuantumCircuit.from_qasm method.
Assumes single-qubit measurement instructions only. Final qubit register
measurement is implicit.
Args:
openqasm_string(str): an OpenQASM program, as a string, as defined by
IBM Qiskit.
Returns:
Circuit: corresponding quantum circuit in the abstract format.
"""
# Get dictionary of gate mapping, as the reverse dictionary of abs -> openqasm translation
GATE_OPENQASM = get_openqasm_gates()
gate_mapping = {v: k for k, v in GATE_OPENQASM.items()}
def parse_param(s):
""" Parse parameter as either a float or a string if it's not a float """
try:
return float(s)
except ValueError:
return s
# Get number of qubits, extract gate operations
n_qubits = int(re.findall(r'qreg q\[(\d+)\];', openqasm_str)[0])
openqasm_gates = openqasm_str.split(f"qreg q[{n_qubits}];\ncreg c[{n_qubits}];")[-1]
openqasm_gates = [instruction for instruction in openqasm_gates.split("\n") if instruction]
# Translate gates
abs_circ = Circuit()
for openqasm_gate in openqasm_gates:
# Extract gate name, qubit indices and parameter value (single parameter for now)
gate_name = re.split(r'\s|\(', openqasm_gate)[0]
qubit_indices = [int(index) for index in re.findall(r'q\[(\d+)\]', openqasm_gate)]
parameters = [parse_param(index) for index in re.findall(r'\((.*)\)', openqasm_gate)]
# TODO: controlled operation, will need to store value in classical register
# bit_indices = [int(index) for index in re.findall('c\[(\d+)\]', openqasm_gate)]
if gate_name in {"h", "x", "y", "z", "s", "t", "measure"}:
gate = Gate(gate_mapping[gate_name], qubit_indices[0])
elif gate_name in {"rx", "ry", "rz", "p"}:
gate = Gate(gate_mapping[gate_name], qubit_indices[0], parameter=eval(str(parameters[0])))
# TODO: Rethink the use of enums for gates to set the equality CX=CNOT and enable other refactoring
elif gate_name in {"cx", "cz", "cy"}:
gate = Gate(gate_mapping[gate_name], qubit_indices[1], control=qubit_indices[0])
elif gate_name in {"crz", "cp"}:
gate = Gate(gate_mapping[gate_name], qubit_indices[1], control=qubit_indices[0], parameter=eval(str(parameters[0])))
elif gate_name in {"swap"}:
gate = Gate(gate_mapping[gate_name], [qubit_indices[0], qubit_indices[1]])
elif gate_name in {"cswap"}:
gate = Gate(gate_mapping[gate_name], [qubit_indices[1], qubit_indices[2]], control=qubit_indices[0])
else:
raise ValueError(f"Gate '{gate_name}' not supported with openqasm translation")
abs_circ.add_gate(gate)
return abs_circ