-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathqasm_manipulation.py
340 lines (281 loc) · 10.4 KB
/
qasm_manipulation.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
import re
import numpy as np
import uuid
from typing import Dict, Any, List
def remove_all_measurements(qasm_program: str):
lines = qasm_program.split("\n")
non_measurement_lines = [
line
for line in lines
if re.match(r"\s*measure\s*", line) is None
]
measurement_lines = [
line
for line in lines
if not re.match(r"\s*measure\s*", line) is None
]
new_qasm_program = "\n".join(non_measurement_lines)
removed_measurement_section = "\n".join(measurement_lines)
return new_qasm_program, removed_measurement_section
def detect_registers(qasm_program: str):
"""Detect the registers in the circuit."""
# list with circuits and their number of qubits
# example. registers = [("qreg", "q", 5), ("creg", "c", 2)]
registers = list(zip(
re.findall(r"\s*([c|q]reg)\s*[a-zA-Z]+\[\d+\]\s*;", qasm_program),
re.findall(r"\s*[c|q]reg\s*([a-zA-Z]+)\[\d+\]\s*;", qasm_program),
[
int(e) for e in
re.findall(r"\s*[c|q]reg\s*[a-zA-Z]+\[(\d+)\]\s*;", qasm_program)
]
))
return registers
def get_first_and_only_quantum_register(qasm_program: str):
"""Get the first and only quantum register.
Format: (qreg|creq, name, number_of_qubits)
"""
quantum_registers = [
reg
for reg in detect_registers(qasm_program)
if reg[0] == "qreg"
]
if len(quantum_registers) != 1:
raise ValueError("Only one quantum register is supported.")
first_reg = {
"type": quantum_registers[0][0],
"name": quantum_registers[0][1],
"n_qubits": quantum_registers[0][2]
}
return first_reg
def append_1Q_gate(qasm_program: str, gate: str, qubits: List[int]):
"""Append the given gate to all the qubits in the list."""
first_qreg = get_first_and_only_quantum_register(qasm_program)
new_qasm_program = qasm_program
for qubit in qubits:
new_qasm_program += "\n" + gate + " " + first_qreg["name"] + "[" + str(qubit) + "];"
# not gate: "x q[0];"
return new_qasm_program
# User for metamorphic testing
def get_n_qubits(qasm_content: str) -> int:
m = re.search("qreg q\[(\d*)", qasm_content)
if m:
return int(m.group(1))
return -1
def replace_all_reg_occurrences(qasm_content: str, start: str, end: str) -> str:
qasm_content = qasm_content.replace(f"q[{start}]", f"q[{end}]")
qasm_content = qasm_content.replace(f"c[{start}]", f"c[{end}]")
return qasm_content
def scramble_qubits(qasm_content: str, qubits_mapping: Dict[int, int]):
"""Swap the qubits based on order."""
pairs = [{"start": k, "end": v} for k, v in qubits_mapping.items()]
triplets = [ {"tmp": uuid.uuid4().hex, **p} for p in pairs]
print("-" * 80)
print(qubits_mapping)
for triplet in triplets:
qasm_content = replace_all_reg_occurrences(
qasm_content, start=triplet["start"], end=triplet["tmp"])
for triplet in triplets:
qasm_content = replace_all_reg_occurrences(
qasm_content, start=triplet["tmp"], end=triplet["end"])
return qasm_content
def create_random_mapping(qasm_content: str, seed: int = None) -> Dict[int, int]:
max_qubits = get_n_qubits(qasm_content)
if seed:
np.random.seed(seed)
start_qubits = np.arange(max_qubits)
np.random.shuffle(start_qubits)
end_qubits = np.arange(max_qubits)
np.random.shuffle(end_qubits)
return {f: s for f, s in zip(start_qubits, end_qubits)}
def read_str_with_mapping(bitstring: str, direct_mapping: Dict[int, int]):
"""Given a bitstring convert it to the original mapping."""
n_bits = len(bitstring)
return "".join([bitstring[direct_mapping[i]] for i in range(n_bits)])
def convert_result_to_mapping(result: Dict[str, int], qubits_mapping: Dict[int, int]):
"""Convert the result via the given mapping.
because a qubit maping will make also the result scrambled thus we have
to reverse the mapping and read the results.
"""
return {
read_str_with_mapping(bitstring, qubits_mapping): freq
for bitstring, freq in result.items()
}
qasm_content = """
OPENQASM 2.0;
include "qelib1.inc";
qreg q[6];
creg c[6];
// This initializes 6 quantum registers and 6 classical registers.
h q[0];
h q[1];
h q[2];
rx(3.09457993732866) q[2];
cx q[1], q[0];
cx q[2], q[1];
// The first 3 qubits are put into superposition states.
"""
qasm_content_without_qubit_0 = """
OPENQASM 2.0;
include "qelib1.inc";
qreg q[6];
creg c[6];
// This initializes 6 quantum registers and 6 classical registers.
h q[1];
h q[2];
rx(3.09457993732866) q[2];
cx q[2], q[1];
// The first 3 qubits are put into superposition states.
"""
qasm_complete = """
OPENQASM 2.0;
include "qelib1.inc";
qreg q[10];
creg c[10];
cx q[9], q[6];
ry(0.9801424781769557) q[1];
cx q[8], q[5];
ry(6.094123332392967) q[0];
rz(1.1424399624340646) q[2];
cx q[3], q[5];
ry(0.528458648415554) q[1];
ry(5.16389904909091) q[0];
barrier q;
measure q -> c;
"""
qasm_head = """OPENQASM 2.0;
include "qelib1.inc";
qreg q[10];
creg c[10];
cx q[9], q[6];
ry(0.9801424781769557) q[1];
barrier q;
measure q -> c;"""
qasm_tail = """OPENQASM 2.0;
include "qelib1.inc";
qreg q[10];
creg c[10];
ry(0.528458648415554) q[1];
ry(5.16389904909091) q[0];
barrier q;
measure q -> c;"""
class QasmModifier(object):
def __init__(self, original_qasm):
self.original_qasm = original_qasm
self.lines = original_qasm.split("\n")
self.lines = [l for l in self.lines if not l.strip() == ""]
self.hidable_lines = [
no_line
for no_line, line in enumerate(self.lines)
if not (
line.startswith("OPENQASM") or
line.startswith("include") or
line.startswith("qreg") or
line.startswith("creg") or
line.startswith("//") or
line.startswith("barrier") or
line.startswith("measure")
)
]
self.mask_hide = np.zeros(len(self.lines), dtype=bool)
self.registers = detect_registers(self.original_qasm)
self._detect_qubits()
self._trace_every_statement_to_a_register()
def _detect_qubits(self):
self.q_registers = [r for r in self.registers if r[0] == "qreg"]
only_qubit_numbers = [r[2] for r in self.q_registers]
self.number_qubits = sum(only_qubit_numbers)
self.qubits = [
[(r[1], i) for i in range(r[2])]
for r in self.q_registers
]
# flatten a list
self.qubits = [item for sublist in self.qubits for item in sublist]
def get_available_qubits(self):
print(self.qubits)
return list(self.qubits)
def _trace_every_statement_to_a_register(self):
"""Map every line in the source code to a specific register-qubit."""
qubits_2_lines = {}
# print("Qubits: ", self.qubits)
for (register_name, qubit_pos) in self.qubits:
for no_line, line in enumerate(self.lines):
if re.search(rf"\s*{register_name}\[\s*{qubit_pos}*\s*\]\s*", line) is not None:
vectorized_name = f"{register_name}-{qubit_pos}"
current_lines = qubits_2_lines.get(vectorized_name, [])
current_lines.append(no_line)
qubits_2_lines[vectorized_name] = current_lines
# print("qubits_2_lines: ", qubits_2_lines)
self.qubits_2_lines = qubits_2_lines
def hide_qubit(self, register_name, qubit_pos):
"""Hide all the operations involving the passed qubit."""
vectorized_name = f"{register_name}-{qubit_pos}"
lines_to_hide = self.qubits_2_lines[vectorized_name]
for line in lines_to_hide:
self.mask_hide[line] = True
def hide_after_line(self, line_no):
"""Hide all the operations after the passed line number."""
if line_no < len(self.lines):
self.mask_hide[line_no:] = True
if self.lines[-2] == "barrier q;":
self.mask_hide[-2] = False
if self.lines[-1] == "measure q -> c;":
self.mask_hide[-1] = False
def hide_before_line(self, line_no):
"""Hide all the operations before the passed line number."""
for i in range(line_no):
if i in self.hidable_lines:
self.mask_hide[i] = True
def get_visible(self):
new_text = "\n".join([
line
for no_line, line in enumerate(self.lines)
if not self.mask_hide[no_line]
])
#print(new_text)
return new_text
def get_available_lines(self):
return self.hidable_lines
def set_visible_only(self, list_visible_lines):
# set all as hidden
self.mask_hide = np.ones(len(self.lines), dtype=bool)
# restore visibility for lines passed in the list
for line_no in list_visible_lines:
self.mask_hide[line_no] = False
# restore the visibility for all the essential lines
# namely those which are not hidable
for i in range(len(self.lines)):
if i not in self.hidable_lines:
self.mask_hide[i] = False
def reset_mask(self):
self.mask_hide = np.zeros(len(self.lines), dtype=bool)
def test_detect_registers():
assert detect_registers(qasm_content) == [
('qreg', 'q', 6), ('creg', 'c', 6)
]
def test_append_1Q_gate():
new_qasm = append_1Q_gate(qasm_content, "x", list(range(6)))
lines = new_qasm.split("\n")
for i in range(1, 7):
c_line = lines[-i]
assert c_line == "x q[" + str(6-i) + "];", c_line
def test_qasm_modifier():
qasm_modifier = QasmModifier(qasm_content)
expected_result = list(zip([c for c in "qqqqqq"], list(range(6))))
assert qasm_modifier.get_available_qubits() == expected_result
def test_hide():
qasm_modifier = QasmModifier(qasm_content)
qasm_modifier.hide_qubit("q", 0)
expected_output = qasm_content_without_qubit_0
assert qasm_modifier.get_visible() == expected_output
def test_hide_tail():
qasm_modifier = QasmModifier(qasm_complete)
qasm_modifier.hide_after_line(6)
expected_output = qasm_head
assert qasm_modifier.get_visible() == expected_output
def test_hide_head():
qasm_modifier = QasmModifier(qasm_complete)
qasm_modifier.hide_before_line(10)
expected_output = qasm_head
assert qasm_modifier.get_visible() == expected_output
if __name__ == "__main__":
test_hide_tail()