Skip to content

Commit ad7d4f1

Browse files
committed
QASM compiler now includes measurement instructions and started preparations for QASM integration into ZI.
1 parent dc931e9 commit ad7d4f1

5 files changed

Lines changed: 391 additions & 33 deletions

File tree

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import numpy as np
2+
from sqdtoolz.Experiments.Experimental.ExpZIqubit import ExpZIqubit
3+
from sqdtoolz.Variable import VariablePropertyTransient
4+
from sqdtoolz.HAL.WaveformGeneric import *
5+
from sqdtoolz.HAL.WaveformSegments import *
6+
from sqdtoolz.Utilities.DataFitting import *
7+
from sqdtoolz.Experiments.Experimental.ExpCalibGE import *
8+
from sqdtoolz.Utilities.QubitGates import QubitGatesBase
9+
import json
10+
from sqdtoolz.Experiments.Experimental.ZI import single_qubit_gates_sweep
11+
from sqdtoolz.Utilities.QubitGates import QubitGatesBase
12+
from sqdtoolz.Utilities.ParserOpenQASM import ParserOpenQASM
13+
14+
class ExpZIQASM(ExpZIqubit):
15+
def __init__(self, name, expt_config, hal_QPU, qubit_ids, qasm_file_path, **kwargs):
16+
self._qubit_datasets = qubit_ids
17+
18+
self._hal_QPU = hal_QPU
19+
20+
self._dont_show_plot = kwargs.pop('dont_show_plot', False)
21+
assert (not 'update' in kwargs) or ('update' in kwargs and not kwargs['update']), "Don't set 'update=True'. The updates shall be done by calling update_qubit after running the experiment."
22+
kwargs['update'] = False
23+
24+
kwargs['coordinate_system'] = kwargs.get('coordinate_system', 'RH')
25+
assert kwargs['coordinate_system'] in ['LH', 'RH'], "The 'coordinate_system' must be either LH or RH for left/right handed."
26+
27+
self._poqasm = ParserOpenQASM(qasm_file_path)
28+
self._poqasm.compiled_operations
29+
30+
kwargs['gate_lists'] = []
31+
for cur_seq in self._gate_seqs:
32+
kwargs['gate_lists'].append([cur_seq]*len(qubit_ids))
33+
34+
super().__init__(name, expt_config, single_qubit_gates_sweep, hal_QPU, qubit_ids, **kwargs)
35+
36+
def _post_process(self, data):
37+
pass
38+

sqdtoolz/Utilities/ParserOpenQASM.py

Lines changed: 52 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -200,12 +200,12 @@ def p_expressions(self, p):
200200
def p_functioncall(self, p):
201201
'''statement : functionsignature params SEMICOLON
202202
'''
203-
p[0] = ('functioncall', {'name':p[1], 'arguments':p[2]})
203+
p[0] = ('functioncall', {'type':'function', 'name':p[1], 'qargs':p[2]})
204204

205205
def p_functioncall_global(self, p):
206206
'''statement : functionsignature indexedparams SEMICOLON
207207
'''
208-
p[0] = ('functioncall', {'name':p[1], 'arguments':p[2]})
208+
p[0] = ('functioncall', {'type':'function', 'name':p[1], 'qargs':p[2]})
209209

210210
def p_version(self, p):
211211
'''globalstatement : VERSION NUMBER SEMICOLON
@@ -245,14 +245,14 @@ def p_measure(self, p):
245245
| ID LARRAY NUMBER RARRAY ASSIGN MEASURE ID LARRAY NUMBER RARRAY SEMICOLON
246246
'''
247247
if len(p) == 5:
248-
p[0] = ('measure', p[4], p[1])
248+
p[0] = ('measure', {'type':'measure', 'qargs':[p[4]], 'store':p[1]})
249249
elif len(p) == 8:
250250
if p[2] == '[':
251-
p[0] = ('measure', p[7], (p[1], p[3]))
251+
p[0] = ('measure', {'type':'measure', 'qargs':[p[7]], 'store':(p[1], p[3])})
252252
else:
253-
p[0] = ('measure', (p[4], p[6]), p[1])
253+
p[0] = ('measure', {'type':'measure', 'qargs':[(p[4], p[6])], 'store':p[1]})
254254
else:
255-
p[0] = ('measure', (p[7], p[9]), (p[1], p[3]))
255+
p[0] = ('measure', {'type':'measure', 'qargs':[(p[7], p[9])], 'store':(p[1], p[3])})
256256
#
257257
#NOTE: The measurement tuples are: (Qubit to Measure, Classical Register to Store)
258258
def p_measure_old(self, p):
@@ -262,14 +262,14 @@ def p_measure_old(self, p):
262262
| MEASURE ID LARRAY NUMBER RARRAY ASSIGNOLD ID LARRAY NUMBER RARRAY SEMICOLON
263263
'''
264264
if len(p) == 5:
265-
p[0] = ('measure', p[2], p[4])
265+
p[0] = ('measure', {'type':'measure', 'qargs':[p[2]], 'store':p[4]})
266266
elif len(p) == 8:
267267
if p[3] == '[':
268-
p[0] = ('measure', (p[2], p[4]), p[7])
268+
p[0] = ('measure', {'type':'measure', 'qargs':[(p[2], p[4])], 'store':p[7]})
269269
else:
270-
p[0] = ('measure', p[2], (p[4], p[6]))
270+
p[0] = ('measure', {'type':'measure', 'qargs':[p[2]], 'store':(p[4], p[6])})
271271
else:
272-
p[0] = ('measure', (p[2], p[4]), (p[7], p[9]))
272+
p[0] = ('measure', {'type':'measure', 'qargs':[(p[2], p[4])], 'store':(p[7], p[9])})
273273

274274

275275
def p_statements(self, p):
@@ -341,16 +341,7 @@ def __init__(self, main_file: str, source_dirs: List[str]):
341341
self._parse_file(cur_file)
342342
self.compiled_operations = self._final_compile()
343343

344-
def _get_include_tree(self, cur_includes_stack: List[str], overall_includes: List[str], source_dirs: List[str]):
345-
current_file = cur_includes_stack[-1]
346-
cur_includes = self._extract_includes(current_file, source_dirs)
347-
for cur_include in cur_includes:
348-
assert not cur_include in cur_includes_stack, f"There is a circular dependency with {cur_include}."
349-
self._get_include_tree(cur_includes_stack + [cur_include], overall_includes, source_dirs)
350-
overall_includes.append(current_file)
351-
return
352-
353-
def _extract_includes(self, file_path: str, source_dirs: List[str]):
344+
def _find_file(self, file_path, source_dirs):
354345
if not os.path.exists(file_path):
355346
found = False
356347
for cur_source_dir in source_dirs:
@@ -360,6 +351,19 @@ def _extract_includes(self, file_path: str, source_dirs: List[str]):
360351
found = True
361352
break
362353
assert found, f"Could not find file {file_path}"
354+
return file_path
355+
356+
def _get_include_tree(self, cur_includes_stack: List[str], overall_includes: List[str], source_dirs: List[str]):
357+
current_file = cur_includes_stack[-1]
358+
cur_includes = self._extract_includes(current_file, source_dirs)
359+
for cur_include in cur_includes:
360+
assert not cur_include in cur_includes_stack, f"There is a circular dependency with {cur_include}."
361+
self._get_include_tree(cur_includes_stack + [cur_include], overall_includes, source_dirs)
362+
overall_includes.append(self._find_file(current_file, source_dirs))
363+
return
364+
365+
def _extract_includes(self, file_path: str, source_dirs: List[str]):
366+
file_path = self._find_file(file_path, source_dirs)
363367
#################
364368
lines = self._open_file_strip_comments(file_path)
365369
lines = "".join(lines).replace('\n','').split(';')
@@ -402,6 +406,8 @@ def _parse_file(self, file_path: str):
402406
self._bits.append((statement[1], statement[2]))
403407
elif statement[0] == 'functioncall':
404408
self._operations.append(statement[1])
409+
elif statement[0] == 'measure':
410+
self._operations.append(statement[1])
405411

406412
def _eval_expression(self, expr, wildcards):
407413
if isinstance(expr, (int, float)):
@@ -460,18 +466,18 @@ def _replace_func_with_arguments(self, func_name, func_inputs, func_outputs):
460466
for cur_func in self._gate_defs[func_name]['function']:
461467
#Basically iterating over potential ctrl/negctrl etc...
462468
new_func = {'name': self._evaluate_func_signature(cur_func[1]['name'],input_wildcards),
463-
'arguments': [output_wildcards[x] for x in cur_func[1]['arguments']]}
469+
'qargs': [output_wildcards[x] for x in cur_func[1]['qargs']]}
464470
sub_func.append(new_func)
465471
return sub_func
466472

467473

468474
def _eval_func(self, dict_operation):
469475
if len(dict_operation['name']) == 1:
470476
if dict_operation['name'][0][0] == 'U':
471-
return [{'name': [(dict_operation['name'][0][0], dict_operation['name'][0][1])], 'arguments': dict_operation['arguments']}]
477+
return [{'type': 'function', 'name': [(dict_operation['name'][0][0], dict_operation['name'][0][1])], 'qargs': dict_operation['qargs']}]
472478
else:
473479
assert dict_operation['name'][0][0] in self._gate_defs, f"The gate operation {dict_operation['name'][0][0]} is undefined."
474-
temp_func_list = self._replace_func_with_arguments(*dict_operation['name'][0], dict_operation['arguments'])
480+
temp_func_list = self._replace_func_with_arguments(*dict_operation['name'][0], dict_operation['qargs'])
475481
ret_list = []
476482
for cur_func in temp_func_list:
477483
ret_list += self._eval_func(cur_func)
@@ -495,15 +501,15 @@ def _eval_func(self, dict_operation):
495501
else:
496502
assert cur_func[0] in self._gate_defs, f"Function {cur_func[0]} is undefined."
497503
assert len(self._gate_defs[cur_func[0]]['output_args']) == 1, f"The function {cur_func[0]} is not a single-qubit unitary!"
498-
temp_func_list = self._replace_func_with_arguments(*cur_func, dict_operation['arguments'])
504+
temp_func_list = self._replace_func_with_arguments(*cur_func, dict_operation['qargs'])
499505
func_sign_args = []
500506
for cur_func in temp_func_list:
501507
func_sign_args += self._eval_func(cur_func)
502508
ret_list = []
503509
for cur_func in func_sign_args:
504510
le_list = [x for x in new_sign_list]
505511
le_list[found_ind] = cur_func['name'][0]
506-
ret_list.append({'name': le_list, 'arguments': dict_operation['arguments']})
512+
ret_list.append({'type': 'function', 'name': le_list, 'qargs': dict_operation['qargs']})
507513

508514
return ret_list
509515

@@ -523,15 +529,26 @@ def _final_compile(self):
523529
#Process operations
524530
ops = []
525531
for cur_op in self._operations:
526-
ops += self._eval_func(cur_op)
532+
if cur_op['type'] == 'function':
533+
ops += self._eval_func(cur_op)
534+
elif cur_op['type'] == 'measure':
535+
ops.append(cur_op)
527536
#Check qubit registers are valid
528537
for cur_op in ops:
529-
for cur_qubit_arg in cur_op['arguments']:
538+
for cur_qubit_arg in cur_op['qargs']:
530539
if isinstance(cur_qubit_arg, (list, tuple)):
531540
assert cur_qubit_arg[1] < qubit_reg_offset_and_size[cur_qubit_arg[0]][1], f"Index of qubit {cur_qubit_arg[0]}[{cur_qubit_arg[1]}] exceeds register size of {qubit_reg_offset_and_size[cur_qubit_arg[0]][1]}."
541+
# if cur_op['type'] == 'measure':
542+
# if isinstance(cur_op['qubit'], (list, tuple)):
543+
# assert cur_op['qubit'][1] < qubit_reg_offset_and_size[cur_op['qubit'][0]][1], f"Index of qubit {cur_op['qubit'][0]}[{cur_op['qubit'][1]}] exceeds register size of {qubit_reg_offset_and_size[cur_op['qubit'][0]][1]}."
544+
#TODO: Validate classical register sizes as well...
545+
# if isinstance(cur_op['store'], (list, tuple)):
546+
# assert cur_op['store'][1] < qubit_reg_offset_and_size[cur_op['store'][0]][1], f"Index of qubit {cur_op['store'][0]}[{cur_op['store'][1]}] exceeds register size of {qubit_reg_offset_and_size[cur_op['store'][0]][1]}."
547+
532548
#Note that the format of ops is a list of gates where each element is a dictionary with keys:
533549
# name - a list of controls with exactly one unitary in the list
534550
# arguments - the target qubits upon which to apply the controlled unitary gates
551+
#Except if it's a measure type, in which case it has 'qubit' and 'store' keys for the qubit and classical registers respectively.
535552
return ops
536553

537554
def get_axis_angle_from_unitary(self, unitary_angles):
@@ -602,7 +619,7 @@ def plot(self):
602619
for op_ind,cur_op in enumerate(self.compiled_operations):
603620
cur_col = leCols[op_ind%len(leCols)]
604621
cur_qubit_indices = []
605-
for m,cur_qubit in enumerate(cur_op['arguments']):
622+
for m,cur_qubit in enumerate(cur_op['qargs']):
606623
if isinstance(cur_qubit, tuple):
607624
cur_qubit_indices.append(self._qubit_reg_offset_and_size[cur_qubit[0]][0] + cur_qubit[1])
608625
else:
@@ -616,7 +633,9 @@ def plot(self):
616633

617634
for m,x in enumerate(cur_qubit_indices):
618635
qubit_positions[x] = cur_pos
619-
if isinstance(cur_op['name'][m], tuple):
636+
if cur_op['type'] == 'measure':
637+
self._plot_gate(ax, qubit_positions[x], x, '∅', cur_col)
638+
elif isinstance(cur_op['name'][m], tuple):
620639
if cur_op['name'][m][0] == 'U':
621640
axis, angle = self.get_axis_angle_from_unitary(cur_op['name'][m][1])
622641
self._plot_gate(ax, qubit_positions[x], x, self.normalise_name(axis, angle), cur_col)
@@ -632,7 +651,7 @@ def plot(self):
632651
a=0
633652

634653

635-
poqasm = ParserOpenQASM('qpe.qasm',[])
636-
poqasm.plot()
637-
plt.show()
638-
a=0
654+
# poqasm = ParserOpenQASM('qpe.qasm',[])
655+
# poqasm.plot()
656+
# plt.show()
657+
# a=0

tests/ZI_test_QASM.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from sqdtoolz.Utilities.ParserOpenQASM import ParserOpenQASM
2+
import matplotlib.pyplot as plt
3+
4+
poqasm = ParserOpenQASM('tests/ZI_test_QASM.qasm',['tests/'])
5+
poqasm.plot()
6+
plt.show()
7+
a=0

tests/ZI_test_QASM.qasm

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
OPENQASM 2.0;
2+
include "ZI_test_QASM_qelib1.inc";
3+
opaque save_statevector q0,q1,q2,q3;
4+
qubit[1] q0;
5+
qubit[1] q1;
6+
qubit[1] q2;
7+
qubit[1] q3;
8+
qubit q4;
9+
qubit q5;
10+
bit[1] c4;
11+
bit[1] c5;
12+
bit[1] c6;
13+
h q2[0];
14+
x q3[0];
15+
h q0[0];
16+
h q1[0];
17+
h q2[0];
18+
h q1[0];
19+
h q0[0];
20+
c6[0] = measure q2[0];
21+
c5[0] = measure q1[0];
22+
measure q0[0] -> c4[0];
23+
c4[0] = measure q3[0];
24+
save_statevector q0[0],q1[0],q2[0],q3[0];

0 commit comments

Comments
 (0)