Skip to content

Commit 5da2464

Browse files
Add pragma (#35)
* Add pragma * Return better error message * Extend and reuse add_statement * Add test for annotation and ifs * Finalize if before adding annotations * Fix finalize if with annotation and no else * Fix typo
1 parent 7916a80 commit 5da2464

File tree

2 files changed

+62
-7
lines changed

2 files changed

+62
-7
lines changed

oqpy/program.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,15 @@ class ProgramState:
5555
"""
5656

5757
def __init__(self) -> None:
58-
self.body: list[ast.Statement] = []
58+
self.body: list[ast.Statement | ast.Pragma] = []
5959
self.if_clause: Optional[ast.BranchingStatement] = None
6060
self.annotations: list[ast.Annotation] = []
6161

6262
def add_if_clause(self, condition: ast.Expression, if_clause: list[ast.Statement]) -> None:
63+
if_clause_annotations, self.annotations = self.annotations, []
6364
self.finalize_if_clause()
6465
self.if_clause = ast.BranchingStatement(condition, if_clause, [])
66+
self.if_clause.annotations = if_clause_annotations
6567

6668
def add_else_clause(self, else_clause: list[ast.Statement]) -> None:
6769
if self.if_clause is None:
@@ -74,12 +76,15 @@ def finalize_if_clause(self) -> None:
7476
if_clause, self.if_clause = self.if_clause, None
7577
self.add_statement(if_clause)
7678

77-
def add_statement(self, stmt: ast.Statement) -> None:
78-
assert isinstance(stmt, ast.Statement)
79-
self.finalize_if_clause()
80-
if self.annotations:
79+
def add_statement(self, stmt: ast.Statement | ast.Pragma) -> None:
80+
# This function accepts Statement and Pragma even though
81+
# it seems to conflict with the definition of ast.Program.
82+
# Issue raised in https://github.com/openqasm/openqasm/issues/468
83+
assert isinstance(stmt, (ast.Statement, ast.Pragma))
84+
if isinstance(stmt, ast.Statement) and self.annotations:
8185
stmt.annotations = self.annotations + list(stmt.annotations)
8286
self.annotations = []
87+
self.finalize_if_clause()
8388
self.body.append(stmt)
8489

8590

@@ -457,6 +462,13 @@ def measure(
457462
)
458463
return self
459464

465+
def pragma(self, command: str) -> Program:
466+
"""Add a pragma instruction."""
467+
if len(self.stack) != 1:
468+
raise RuntimeError("Pragmas must be global")
469+
self._add_statement(ast.Pragma(command))
470+
return self
471+
460472
def _do_assignment(self, var: AstConvertible, op: str, value: AstConvertible) -> None:
461473
"""Helper function for variable assignment operations."""
462474
if isinstance(var, classical_types.DurationVar):
@@ -537,7 +549,9 @@ def visit_SubroutineDefinition(
537549
node.body = self.process_statement_list(node.body)
538550
self.generic_visit(node, context)
539551

540-
def process_statement_list(self, statements: list[ast.Statement]) -> list[ast.Statement]:
552+
def process_statement_list(
553+
self, statements: list[ast.Statement | ast.Pragma]
554+
) -> list[ast.Statement | ast.Pragma]:
541555
new_list = []
542556
cal_stmts = []
543557
for stmt in statements:

tests/test_directives.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,21 +350,32 @@ def test_binary_expressions():
350350
assert prog.to_qasm() == expected
351351

352352

353-
def test_measure_reset():
353+
def test_measure_reset_pragma():
354354
prog = Program()
355355
q = PhysicalQubits[0]
356356
c = BitVar(name="c")
357357
prog.reset(q)
358+
prog.pragma("CLASSIFIER linear")
358359
prog.measure(q, c)
359360
prog.measure(q)
361+
with oqpy.If(prog, c == 1):
362+
with pytest.raises(RuntimeError):
363+
prog.pragma("Invalid pragma")
364+
prog.gate(q, "x")
365+
prog.pragma("LOAD_MEMORY all")
360366

361367
expected = textwrap.dedent(
362368
"""
363369
OPENQASM 3.0;
364370
bit c;
365371
reset $0;
372+
pragma CLASSIFIER linear
366373
c = measure $0;
367374
measure $0;
375+
if (c == 1) {
376+
x $0;
377+
}
378+
pragma LOAD_MEMORY all
368379
"""
369380
).strip()
370381

@@ -1389,6 +1400,21 @@ def f(prog: Program, x: IntVar) -> IntVar:
13891400
prog.annotate("first-invocation")
13901401
prog.do_expression(f(prog, i))
13911402

1403+
prog.annotate("annotation-before-if")
1404+
with If(prog, i != 0):
1405+
prog.annotate("annotation-in-if")
1406+
prog.gate(q1, "x")
1407+
with oqpy.Else(prog):
1408+
prog.annotate(("annotation-in-else"))
1409+
prog.delay(make_duration(1e-8), q1)
1410+
prog.annotate("annotation-after-if")
1411+
1412+
prog.annotate("annotation-no-else-before-if")
1413+
with If(prog, i != 0):
1414+
prog.annotate("annotation-no-else-in-if")
1415+
prog.gate(q1, "x")
1416+
prog.annotate("annotation-no-else-after-if")
1417+
13921418
prog.annotate("make-for-loop", "with additional info")
13931419
with ForIn(prog, range(1, 1001), "shot") as shot:
13941420
prog.annotate("declaring_j")
@@ -1433,6 +1459,21 @@ def f(int[32] x) -> int[32] {
14331459
qubit q1;
14341460
@first-invocation
14351461
f(i);
1462+
@annotation-before-if
1463+
if (i != 0) {
1464+
@annotation-in-if
1465+
x q1;
1466+
} else {
1467+
@annotation-in-else
1468+
delay[10.0ns] q1;
1469+
}
1470+
@annotation-after-if
1471+
@annotation-no-else-before-if
1472+
if (i != 0) {
1473+
@annotation-no-else-in-if
1474+
x q1;
1475+
}
1476+
@annotation-no-else-after-if
14361477
@make-for-loop with additional info
14371478
for int shot in [1:1000] {
14381479
@declaring_j

0 commit comments

Comments
 (0)