Skip to content

Commit 046266a

Browse files
authored
Create oqpy.Range to allow ForIn ranges with expressions. (#47)
* Add oqpy.Range * Eliminate unnecessary int | AstConvertible
1 parent e2eaf70 commit 046266a

File tree

2 files changed

+47
-5
lines changed

2 files changed

+47
-5
lines changed

oqpy/control_flow.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
from oqpy.program import Program
3939

4040

41-
__all__ = ["If", "Else", "ForIn", "While"]
41+
__all__ = ["If", "Else", "ForIn", "While", "Range"]
4242

4343

4444
@contextlib.contextmanager
@@ -129,18 +129,40 @@ def ForIn(
129129
iterator = (make_duration(i) for i in iterator)
130130

131131
set_declaration = ast.DiscreteSet([to_ast(program, i) for i in iterator])
132-
elif isinstance(iterator, _ClassicalVar):
133-
set_declaration = to_ast(program, iterator)
134-
assert isinstance(set_declaration, ast.Identifier), type(set_declaration)
135132
else:
136-
raise TypeError(f"'{type(iterator)}' object is not iterable")
133+
set_declaration = to_ast(program, iterator)
137134

138135
stmt = ast.ForInLoop(
139136
identifier_type.type_cls(), var.to_ast(program), set_declaration, state.body
140137
)
141138
program._add_statement(stmt)
142139

143140

141+
class Range:
142+
"""AstConvertible which creates an integer range.
143+
144+
Unlike builtin python range, this allows the components to be AstConvertible,
145+
instead of just int.
146+
"""
147+
148+
def __init__(self, start: AstConvertible, stop: AstConvertible, step: AstConvertible = 1):
149+
self.start = start
150+
self.stop = stop
151+
self.step = step
152+
153+
def to_ast(self, program: Program) -> ast.Expression:
154+
"""Convert to an ast.RangeDefinition."""
155+
return ast.RangeDefinition(
156+
to_ast(program, self.start),
157+
ast.BinaryExpression(
158+
lhs=to_ast(program, self.stop),
159+
op=ast.BinaryOperator["-"],
160+
rhs=ast.IntegerLiteral(value=1),
161+
),
162+
to_ast(program, self.step) if self.step != 1 else None,
163+
)
164+
165+
144166
@contextlib.contextmanager
145167
def While(program: Program, condition: OQPyExpression) -> Iterator[None]:
146168
"""Context manager for looping a repeating a portion of a program while a condition is True.

tests/test_directives.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1825,3 +1825,23 @@ def test_constant_conversion():
18251825
"""
18261826
).strip()
18271827
assert prog.to_qasm() == expected
1828+
1829+
1830+
def test_oqpy_range():
1831+
prog = Program()
1832+
sum = oqpy.IntVar(0, "sum")
1833+
with ForIn(prog, range(10), "i") as i:
1834+
with ForIn(prog, oqpy.Range(1, i), "j") as j:
1835+
prog.increment(sum, j)
1836+
expected = textwrap.dedent(
1837+
"""
1838+
OPENQASM 3.0;
1839+
int[32] sum = 0;
1840+
for int i in [0:9] {
1841+
for int j in [1:i - 1] {
1842+
sum += j;
1843+
}
1844+
}
1845+
"""
1846+
).strip()
1847+
assert prog.to_qasm() == expected

0 commit comments

Comments
 (0)