From cda4f706ce07242b5a044fe5706b2d652b218e39 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Mon, 24 Oct 2022 17:46:35 -0400 Subject: [PATCH 1/9] Change barrier/delay default behavior --- oqpy/program.py | 24 ++++++++++++++++++------ tests/test_directives.py | 32 +++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/oqpy/program.py b/oqpy/program.py index 5133680..9323f90 100644 --- a/oqpy/program.py +++ b/oqpy/program.py @@ -384,19 +384,31 @@ def declare( return self def delay( - self, time: AstConvertible, qubits_or_frames: AstConvertible | Iterable[AstConvertible] = () + self, + time: AstConvertible, + qubits_or_frames: AstConvertible | Iterable[AstConvertible] | None = None, ) -> Program: """Apply a delay to a set of qubits or frames.""" - if not isinstance(qubits_or_frames, Iterable): - qubits_or_frames = [qubits_or_frames] + if isinstance(qubits_or_frames, Iterable) and not any(True for _ in qubits_or_frames): + return self + elif qubits_or_frames is None: + ast_qubits_or_frames = [] + else: + if not isinstance(qubits_or_frames, Iterable): + qubits_or_frames = [qubits_or_frames] + ast_qubits_or_frames = map_to_ast(self, qubits_or_frames) ast_duration = to_ast(self, convert_float_to_duration(time)) - ast_qubits_or_frames = map_to_ast(self, qubits_or_frames) self._add_statement(ast.DelayInstruction(ast_duration, ast_qubits_or_frames)) return self - def barrier(self, qubits_or_frames: Iterable[AstConvertible]) -> Program: + def barrier(self, qubits_or_frames: Iterable[AstConvertible] | None = None) -> Program: """Apply a barrier to a set of qubits or frames.""" - ast_qubits_or_frames = map_to_ast(self, qubits_or_frames) + if isinstance(qubits_or_frames, Iterable) and not any(True for _ in qubits_or_frames): + return self + elif qubits_or_frames is None: + ast_qubits_or_frames = [] + else: + ast_qubits_or_frames = map_to_ast(self, qubits_or_frames) self._add_statement(ast.QuantumBarrier(ast_qubits_or_frames)) return self diff --git a/tests/test_directives.py b/tests/test_directives.py index 138d214..08c757e 100644 --- a/tests/test_directives.py +++ b/tests/test_directives.py @@ -862,6 +862,36 @@ def multiply(int[32] x, int[32] y) -> int[32] { _check_respects_type_hints(prog) +def test_barrier_delay_arguments(): + port = PortVar("portname") + frame = FrameVar(port, 1e9, name="frame0") + frame1 = FrameVar(port, 1.5e9, name="frame1") + prog = Program() + prog.barrier() + prog.delay(1e-7) + prog.barrier([]) + prog.delay(2e-7, []) + prog.barrier([frame, frame1]) + prog.delay(3e-7, frame1) + prog.delay(4e-7, [frame, frame1]) + + expected = textwrap.dedent( + """ + OPENQASM 3.0; + port portname; + frame frame0 = newframe(portname, 1000000000.0, 0); + frame frame1 = newframe(portname, 1500000000.0, 0); + barrier; + delay[100.0ns]; + barrier frame0, frame1; + delay[300.0ns] frame1; + delay[400.0ns] frame0, frame1; + """ + ).strip() + + assert prog.to_qasm() == expected + + def test_box_and_timings(): constant = declare_waveform_generator("constant", [("length", duration), ("iq", complex128)]) @@ -1501,8 +1531,8 @@ def _to_oqpy_expression(self): """ OPENQASM 3.0; duration a1 = 100.0ns; - duration a2 = 100.0ns; frame f1; + duration a2 = 100.0ns; a1 = 2; delay[a2] f1; """ From 19978a1234ddd7515e82d5a8a449b65a86f5f9ae Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 11 Aug 2023 11:44:45 -0400 Subject: [PATCH 2/9] Handle cases with generators --- oqpy/program.py | 18 +++++++++++------- tests/test_directives.py | 9 +++++++++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/oqpy/program.py b/oqpy/program.py index 9323f90..42fd6df 100644 --- a/oqpy/program.py +++ b/oqpy/program.py @@ -389,25 +389,29 @@ def delay( qubits_or_frames: AstConvertible | Iterable[AstConvertible] | None = None, ) -> Program: """Apply a delay to a set of qubits or frames.""" - if isinstance(qubits_or_frames, Iterable) and not any(True for _ in qubits_or_frames): - return self - elif qubits_or_frames is None: + ast_duration = to_ast(self, convert_float_to_duration(time)) + + if qubits_or_frames is None: ast_qubits_or_frames = [] else: if not isinstance(qubits_or_frames, Iterable): qubits_or_frames = [qubits_or_frames] + else: + qubits_or_frames = list(qubits_or_frames) + if len(qubits_or_frames) == 0: + return self ast_qubits_or_frames = map_to_ast(self, qubits_or_frames) - ast_duration = to_ast(self, convert_float_to_duration(time)) self._add_statement(ast.DelayInstruction(ast_duration, ast_qubits_or_frames)) return self def barrier(self, qubits_or_frames: Iterable[AstConvertible] | None = None) -> Program: """Apply a barrier to a set of qubits or frames.""" - if isinstance(qubits_or_frames, Iterable) and not any(True for _ in qubits_or_frames): - return self - elif qubits_or_frames is None: + if qubits_or_frames is None: ast_qubits_or_frames = [] else: + qubits_or_frames = list(qubits_or_frames) + if len(qubits_or_frames) == 0: + return self ast_qubits_or_frames = map_to_ast(self, qubits_or_frames) self._add_statement(ast.QuantumBarrier(ast_qubits_or_frames)) return self diff --git a/tests/test_directives.py b/tests/test_directives.py index 08c757e..3c30e44 100644 --- a/tests/test_directives.py +++ b/tests/test_directives.py @@ -875,6 +875,13 @@ def test_barrier_delay_arguments(): prog.delay(3e-7, frame1) prog.delay(4e-7, [frame, frame1]) + def frame_generator(frames): + for frame in frames: + yield frame + + prog.barrier(frame_generator([frame, frame1])) + prog.delay(5e-7, frame_generator([frame, frame1])) + expected = textwrap.dedent( """ OPENQASM 3.0; @@ -886,6 +893,8 @@ def test_barrier_delay_arguments(): barrier frame0, frame1; delay[300.0ns] frame1; delay[400.0ns] frame0, frame1; + barrier frame0, frame1; + delay[500.0ns] frame0, frame1; """ ).strip() From a53b3cac4c0ff772d2958a7925940995c52a3412 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 11 Aug 2023 11:46:00 -0400 Subject: [PATCH 3/9] Process duration ast first --- tests/test_directives.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_directives.py b/tests/test_directives.py index 3c30e44..e86039d 100644 --- a/tests/test_directives.py +++ b/tests/test_directives.py @@ -1540,8 +1540,8 @@ def _to_oqpy_expression(self): """ OPENQASM 3.0; duration a1 = 100.0ns; - frame f1; duration a2 = 100.0ns; + frame f1; a1 = 2; delay[a2] f1; """ From 5368ce0092c073513da36caf36c374570f220bf5 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 11 Aug 2023 12:03:00 -0400 Subject: [PATCH 4/9] check type hints --- tests/test_directives.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_directives.py b/tests/test_directives.py index e86039d..8905ecc 100644 --- a/tests/test_directives.py +++ b/tests/test_directives.py @@ -899,6 +899,7 @@ def frame_generator(frames): ).strip() assert prog.to_qasm() == expected + _check_respects_type_hints(prog) def test_box_and_timings(): From 036fc0fbda49218febe179b53f8e35009856db12 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Mon, 17 Jun 2024 11:27:04 -0400 Subject: [PATCH 5/9] use a single-line assignment --- oqpy/program.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/oqpy/program.py b/oqpy/program.py index 3feb4d3..04b6286 100644 --- a/oqpy/program.py +++ b/oqpy/program.py @@ -428,10 +428,7 @@ def delay( if qubits_or_frames is None: ast_qubits_or_frames = [] else: - if not isinstance(qubits_or_frames, Iterable): - qubits_or_frames = [qubits_or_frames] - else: - qubits_or_frames = list(qubits_or_frames) + qubits_or_frames = list(qubits_or_frames) if isinstance(qubits_or_frames, Iterable) else [qubits_or_frames] if len(qubits_or_frames) == 0: return self ast_qubits_or_frames = map_to_ast(self, qubits_or_frames) From f77c6379291adbed726f2965e959ef74e4a807cf Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Mon, 17 Jun 2024 11:27:26 -0400 Subject: [PATCH 6/9] black --- oqpy/base.py | 6 ++---- oqpy/control_flow.py | 6 ++---- oqpy/program.py | 6 +++++- tests/test_directives.py | 5 ++--- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/oqpy/base.py b/oqpy/base.py index 43d5ea9..5f66c83 100644 --- a/oqpy/base.py +++ b/oqpy/base.py @@ -338,8 +338,7 @@ def expr_matches(a: Any, b: Any) -> bool: class ExpressionConvertible(Protocol): """This is the protocol an object can implement in order to be usable as an expression.""" - def _to_oqpy_expression(self) -> HasToAst: - ... # pragma: no cover + def _to_oqpy_expression(self) -> HasToAst: ... # pragma: no cover @runtime_checkable @@ -355,8 +354,7 @@ class CachedExpressionConvertible(Protocol): _oqpy_cache_key: Hashable - def _to_cached_oqpy_expression(self) -> HasToAst: - ... # pragma: no cover + def _to_cached_oqpy_expression(self) -> HasToAst: ... # pragma: no cover class OQPyUnaryExpression(OQPyExpression): diff --git a/oqpy/control_flow.py b/oqpy/control_flow.py index 15aac93..69162bc 100644 --- a/oqpy/control_flow.py +++ b/oqpy/control_flow.py @@ -85,8 +85,7 @@ def ForIn( program: Program, iterator: Iterable[AstConvertible] | range | AstConvertible, identifier_name: Optional[str], -) -> contextlib._GeneratorContextManager[IntVar]: - ... # pragma: no cover +) -> contextlib._GeneratorContextManager[IntVar]: ... # pragma: no cover @overload @@ -95,8 +94,7 @@ def ForIn( iterator: Iterable[AstConvertible] | range | AstConvertible, identifier_name: Optional[str], identifier_type: type[ClassicalVarT], -) -> contextlib._GeneratorContextManager[ClassicalVarT]: - ... # pragma: no cover +) -> contextlib._GeneratorContextManager[ClassicalVarT]: ... # pragma: no cover @contextlib.contextmanager diff --git a/oqpy/program.py b/oqpy/program.py index 04b6286..da775da 100644 --- a/oqpy/program.py +++ b/oqpy/program.py @@ -428,7 +428,11 @@ def delay( if qubits_or_frames is None: ast_qubits_or_frames = [] else: - qubits_or_frames = list(qubits_or_frames) if isinstance(qubits_or_frames, Iterable) else [qubits_or_frames] + qubits_or_frames = ( + list(qubits_or_frames) + if isinstance(qubits_or_frames, Iterable) + else [qubits_or_frames] + ) if len(qubits_or_frames) == 0: return self ast_qubits_or_frames = map_to_ast(self, qubits_or_frames) diff --git a/tests/test_directives.py b/tests/test_directives.py index 545b156..e25b14e 100644 --- a/tests/test_directives.py +++ b/tests/test_directives.py @@ -1120,6 +1120,7 @@ def test_invalid_extern_declaration(): with pytest.raises(Exception, match="Argument.*"): _ = declare_extern("invalid", [int32]) + def test_defcals(): prog = Program() constant = declare_waveform_generator("constant", [("length", duration), ("iq", complex128)]) @@ -1614,7 +1615,7 @@ class A: def _to_oqpy_expression(self): return DurationVar(1e-7, self.name) - + @dataclass class B: name: str @@ -2646,5 +2647,3 @@ def test_box_with_negative_duration(): with pytest.raises(ValueError, match="Expected a non-negative duration, but got -4e-09"): with Box(prog, -4e-9): pass - - From b7a66a357c8d7c8e6f8c34c491c383438e98d3f2 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Mon, 17 Jun 2024 12:04:56 -0400 Subject: [PATCH 7/9] Bump version to 0.3.6 --- oqpy/__init__.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/oqpy/__init__.py b/oqpy/__init__.py index 8df007a..59e51e9 100644 --- a/oqpy/__init__.py +++ b/oqpy/__init__.py @@ -14,7 +14,7 @@ # limitations under the License. ############################################################################ -__version__ = "0.3.5" +__version__ = "0.3.6" from oqpy.classical_types import * from oqpy.control_flow import * from oqpy.program import * diff --git a/pyproject.toml b/pyproject.toml index 0493f25..c89d251 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "oqpy" -version = "0.3.5" +version = "0.3.6" description = "Generating OpenQASM 3 + OpenPulse in Python" authors = ["OQpy Contributors "] license = "Apache-2.0" From 4b4d278e1e2c89fa9342b558e70206d746a4c658 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 21 Jun 2024 10:53:27 -0400 Subject: [PATCH 8/9] add soft deprecation --- oqpy/base.py | 6 ++- oqpy/control_flow.py | 6 ++- oqpy/program.py | 26 +++++++++- tests/test_directives.py | 100 +++++++++++++++++++++++++++++---------- 4 files changed, 106 insertions(+), 32 deletions(-) diff --git a/oqpy/base.py b/oqpy/base.py index 82b0bd4..19d3a9f 100644 --- a/oqpy/base.py +++ b/oqpy/base.py @@ -338,7 +338,8 @@ def expr_matches(a: Any, b: Any) -> bool: class ExpressionConvertible(Protocol): """This is the protocol an object can implement in order to be usable as an expression.""" - def _to_oqpy_expression(self) -> AstConvertible: ... # pragma: no cover + def _to_oqpy_expression(self) -> AstConvertible: + ... # pragma: no cover @runtime_checkable @@ -354,7 +355,8 @@ class CachedExpressionConvertible(Protocol): _oqpy_cache_key: Hashable - def _to_cached_oqpy_expression(self) -> HasToAst: ... # pragma: no cover + def _to_cached_oqpy_expression(self) -> HasToAst: + ... # pragma: no cover class OQPyUnaryExpression(OQPyExpression): diff --git a/oqpy/control_flow.py b/oqpy/control_flow.py index 69162bc..15aac93 100644 --- a/oqpy/control_flow.py +++ b/oqpy/control_flow.py @@ -85,7 +85,8 @@ def ForIn( program: Program, iterator: Iterable[AstConvertible] | range | AstConvertible, identifier_name: Optional[str], -) -> contextlib._GeneratorContextManager[IntVar]: ... # pragma: no cover +) -> contextlib._GeneratorContextManager[IntVar]: + ... # pragma: no cover @overload @@ -94,7 +95,8 @@ def ForIn( iterator: Iterable[AstConvertible] | range | AstConvertible, identifier_name: Optional[str], identifier_type: type[ClassicalVarT], -) -> contextlib._GeneratorContextManager[ClassicalVarT]: ... # pragma: no cover +) -> contextlib._GeneratorContextManager[ClassicalVarT]: + ... # pragma: no cover @contextlib.contextmanager diff --git a/oqpy/program.py b/oqpy/program.py index da775da..6857032 100644 --- a/oqpy/program.py +++ b/oqpy/program.py @@ -419,11 +419,23 @@ def declare( def delay( self, + qubits_or_frames: AstConvertible | Iterable[AstConvertible] | None, time: AstConvertible, - qubits_or_frames: AstConvertible | Iterable[AstConvertible] | None = None, ) -> Program: """Apply a delay to a set of qubits or frames.""" - ast_duration = to_ast(self, convert_float_to_duration(time, require_nonnegative=True)) + if isinstance(time, (quantum_types.Qubit, FrameVar, Iterable)): + # Deprecation branch + ast_duration = to_ast( + self, convert_float_to_duration(qubits_or_frames, require_nonnegative=True) + ) + qubits_or_frames = time + warnings.warn( + "Arguments have been automatically swapped. qubits_or_frames is now the first argument. " + "Please update your code.", + DeprecationWarning, + ) + else: + ast_duration = to_ast(self, convert_float_to_duration(time, require_nonnegative=True)) if qubits_or_frames is None: ast_qubits_or_frames = [] @@ -439,6 +451,16 @@ def delay( self._add_statement(ast.DelayInstruction(ast_duration, ast_qubits_or_frames)) return self + def delay_all( + self, + time: AstConvertible, + ) -> Program: + """Apply a delay to all qubits or frames.""" + ast_duration = to_ast(self, convert_float_to_duration(time, require_nonnegative=True)) + + self._add_statement(ast.DelayInstruction(ast_duration, [])) + return self + def barrier(self, qubits_or_frames: Iterable[AstConvertible] | None = None) -> Program: """Apply a barrier to a set of qubits or frames.""" if qubits_or_frames is None: diff --git a/tests/test_directives.py b/tests/test_directives.py index 728f2b5..03393eb 100644 --- a/tests/test_directives.py +++ b/tests/test_directives.py @@ -30,7 +30,13 @@ import oqpy from oqpy import * -from oqpy.base import OQPyBinaryExpression, OQPyExpression, expr_matches, logical_and, logical_or +from oqpy.base import ( + OQPyBinaryExpression, + OQPyExpression, + expr_matches, + logical_and, + logical_or, +) from oqpy.classical_types import OQIndexExpression from oqpy.quantum_types import PhysicalQubits from oqpy.timing import OQDurationLiteral @@ -310,7 +316,7 @@ def test_non_trivial_array_access(): one = oqpy.IntVar(name="one", init_expression=1) with oqpy.ForIn(prog, range(4), "idx") as idx: - prog.delay(zero_to_one[idx + one] + one_second, frame) + prog.delay(frame, zero_to_one[idx + one] + one_second) prog.set(zero_to_one[idx], 5) expected = textwrap.dedent( @@ -500,7 +506,7 @@ def test_add_incomptible_type(): delay = oqpy.DurationVar(10e-9, name="d") f = oqpy.FloatVar(5e-9, "f") - prog.delay(delay + f, frame) + prog.delay(frame, delay + f) # Note the automatic conversion of float to duration. Do note that OpenQASM spec does not allows # `float * duration` but does allow for `const float * duration`. So this example is not @@ -691,7 +697,7 @@ def test_for_in_var_types(): delays = [1e-9, 2e-9, 5e-9, 10e-9, 1e-6] with oqpy.ForIn(program, delays, "d", DurationVar) as delay: - program.delay(delay, frame) + program.delay(frame, delay) expected = textwrap.dedent( """ @@ -818,7 +824,7 @@ def declare(prog: Program, x: IntVar): @subroutine def delay50ns(prog: Program, q: Qubit) -> None: - prog.delay(50e-9, q) + prog.delay(q, 50e-9) q = PhysicalQubits[0] prog.do_expression(delay50ns(prog, q)) @@ -891,7 +897,7 @@ def test_subroutine_order(): @subroutine def delay50ns(prog: Program, q: Qubit) -> None: - prog.delay(50e-9, q) + prog.delay(q, 50e-9) @subroutine def multiply(prog: Program, x: IntVar, y: IntVar) -> IntVar: @@ -923,24 +929,37 @@ def multiply(int[32] x, int[32] y) -> int[32] { def test_barrier_delay_arguments(): + @dataclass + class Q: + num: int + + def _to_oqpy_expression(self): + return Qubit(f"q{self.num}") + port = PortVar("portname") frame = FrameVar(port, 1e9, name="frame0") frame1 = FrameVar(port, 1.5e9, name="frame1") prog = Program() prog.barrier() - prog.delay(1e-7) + prog.delay_all(1e-7) + prog.delay(None, 1.5e-7) prog.barrier([]) - prog.delay(2e-7, []) + prog.delay([], 2e-7) prog.barrier([frame, frame1]) - prog.delay(3e-7, frame1) - prog.delay(4e-7, [frame, frame1]) + prog.delay(frame1, 3e-7) + prog.delay([frame, frame1], 4e-7) def frame_generator(frames): for frame in frames: yield frame prog.barrier(frame_generator([frame, frame1])) - prog.delay(5e-7, frame_generator([frame, frame1])) + prog.delay(frame_generator([frame, frame1]), 5e-7) + + prog.delay(Q(1), 6e-7) + + with pytest.warns(DeprecationWarning, match="Arguments have been automatically swapped."): + prog.delay(7e-7, frame) expected = textwrap.dedent( """ @@ -948,13 +967,42 @@ def frame_generator(frames): port portname; frame frame0 = newframe(portname, 1000000000.0, 0); frame frame1 = newframe(portname, 1500000000.0, 0); + qubit q1; barrier; delay[100.0ns]; + delay[150.0ns]; barrier frame0, frame1; delay[300.0ns] frame1; delay[400.0ns] frame0, frame1; barrier frame0, frame1; delay[500.0ns] frame0, frame1; + delay[600.0ns] q1; + delay[700.0ns] frame0; + """ + ).strip() + + assert prog.to_qasm() == expected + _check_respects_type_hints(prog) + + +@pytest.mark.xfail() +def test_non_catching_deprecation(): + @dataclass + class Q: + num: int + + def _to_oqpy_expression(self): + return Qubit(f"q{self.num}") + + prog = Program() + with pytest.warns(UserWarning, match="Arguments have been automatically swapped."): + prog.delay(8e-7, Q(1)) + + expected = textwrap.dedent( + """ + OPENQASM 3.0; + qubit q1; + delay[800.0ns] q1; """ ).strip() @@ -970,7 +1018,7 @@ def test_box_and_timings(): prog = Program() with Box(prog, 500e-9): prog.play(frame, constant(100e-9, 0.5)) - prog.delay(200e-7, frame) + prog.delay(frame, 200e-7) prog.play(frame, constant(100e-9, 0.5)) with Box(prog): @@ -1396,12 +1444,12 @@ def test_ramsey_example(): prog.declare(tppi_angle) with ForIn(prog, range(81), "delay_increment") as delay_increment: ( - prog.delay(100e-6) + prog.delay_all(100e-6) .set_phase(q_frame, 0) .set_phase(rx_frame, 0) .set_phase(tx_frame, 0) .gate(q2, "x90") - .delay(ramsey_delay) + .delay_all(ramsey_delay) .shift_phase(q_frame, tppi_angle) .gate(q2, "x90") .gate(q2, "readout") @@ -1498,7 +1546,7 @@ def test_rabi_example(): with ForIn(prog, range(1, 1001), "shot") as shot: prog.set_scale(q0_transmon_xy_frame, -0.2) with ForIn(prog, range(1, 102), "amplitude") as amplitude: - prog.delay(200e-6, frames) + prog.delay(frames, 200e-6) for frame in frames: prog.set_phase(frame, 0) ( @@ -1550,7 +1598,7 @@ def test_program_add(): prog1 = Program() constant = declare_waveform_generator("constant", [("length", duration), ("iq", complex128)]) - prog1.delay(1e-6) + prog1.delay_all(1e-6) prog2 = Program() q1 = PhysicalQubits[1] @@ -1639,9 +1687,9 @@ def __rmul__(self, other): prog = Program() prog.set(A("a1"), 2) prog.set(FloatVar(name="c1"), 3 * C()) - prog.delay(A("a2"), frame) - prog.delay(B("b1"), frame) - prog.delay(C(), frame) + prog.delay(frame, A("a2")) + prog.delay(frame, B("b1")) + prog.delay(frame, C()) expected = textwrap.dedent( """ OPENQASM 3.0; @@ -1686,7 +1734,7 @@ def _to_cached_oqpy_expression(self): dur = A("dur") prog = Program() prog.set(dur, 2) - prog.delay(dur, frame) + prog.delay(frame, dur) prog.set(dur, 3) expected = textwrap.dedent( """ @@ -1899,7 +1947,7 @@ def f(prog: Program, x: IntVar) -> IntVar: prog.gate(q1, "x") with oqpy.Else(prog): prog.annotate(("annotation-in-else")) - prog.delay(convert_float_to_duration(1e-8), q1) + prog.delay(q1, convert_float_to_duration(1e-8)) prog.annotate("annotation-after-if") prog.annotate("annotation-no-else-before-if") @@ -2099,7 +2147,7 @@ def test_duration_literal_arithmetic(): assert isinstance(repeated_delay, OQPyExpression) assert repeated_delay.type == ast.DurationType() - program.delay(repeated_delay, frame) + program.delay(frame, repeated_delay) program.shift_phase(frame, 2 * oqpy.pi * (delay_time / one_second)) expected = textwrap.dedent( @@ -2207,7 +2255,7 @@ def test_ramsey_example_blog(): ( ramsey_prog.reset(qubit) # prepare in ground state .gate(qubit, "x90") # pi/2 pulse - .delay(delay_time, qubit) # variable delay + .delay(qubit, delay_time) # variable delay .gate(qubit, "x90") # pi/2 pulse .measure(qubit) # final measurement .increment(delay_time, 100e-9) @@ -2235,7 +2283,7 @@ def test_ramsey_example_blog(): ) with oqpy.defcal(defcals_prog, qubit, "reset"): - defcals_prog.delay(1e-3) # reset to ground state by waiting 1 millisecond + defcals_prog.delay_all(1e-3) # reset to ground state by waiting 1 millisecond with oqpy.defcal(defcals_prog, qubit, "measure"): defcals_prog.play(tx_frame, constant_waveform(2.4e-6, 0.2)) @@ -2354,7 +2402,7 @@ def test_duration_coercion(): frame = FrameVar(name="f1") prog = Program() v = oqpy.FloatVar(0.1, name="v") - prog.delay(v * 100e-9, frame) + prog.delay(frame, v * 100e-9) d = oqpy.DurationVar(100e-9, name="d") prog.shift_phase(frame, d * 1e4) expected = textwrap.dedent( @@ -2656,7 +2704,7 @@ def test_delay_with_negative_duration(): port = oqpy.PortVar(name="my_port") frame = oqpy.FrameVar(name="my_frame", port=port, frequency=1e9, phase=0) with pytest.raises(ValueError, match="Expected a non-negative duration, but got -4e-09"): - prog.delay(-4e-9, frame) + prog.delay(frame, -4e-9) def test_box_with_negative_duration(): From e8b3d79a210482471d3237a5492ab3c80466993d Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 21 Jun 2024 10:57:46 -0400 Subject: [PATCH 9/9] reuse delay --- oqpy/base.py | 6 ++---- oqpy/control_flow.py | 6 ++---- oqpy/program.py | 5 +---- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/oqpy/base.py b/oqpy/base.py index 19d3a9f..82b0bd4 100644 --- a/oqpy/base.py +++ b/oqpy/base.py @@ -338,8 +338,7 @@ def expr_matches(a: Any, b: Any) -> bool: class ExpressionConvertible(Protocol): """This is the protocol an object can implement in order to be usable as an expression.""" - def _to_oqpy_expression(self) -> AstConvertible: - ... # pragma: no cover + def _to_oqpy_expression(self) -> AstConvertible: ... # pragma: no cover @runtime_checkable @@ -355,8 +354,7 @@ class CachedExpressionConvertible(Protocol): _oqpy_cache_key: Hashable - def _to_cached_oqpy_expression(self) -> HasToAst: - ... # pragma: no cover + def _to_cached_oqpy_expression(self) -> HasToAst: ... # pragma: no cover class OQPyUnaryExpression(OQPyExpression): diff --git a/oqpy/control_flow.py b/oqpy/control_flow.py index 15aac93..69162bc 100644 --- a/oqpy/control_flow.py +++ b/oqpy/control_flow.py @@ -85,8 +85,7 @@ def ForIn( program: Program, iterator: Iterable[AstConvertible] | range | AstConvertible, identifier_name: Optional[str], -) -> contextlib._GeneratorContextManager[IntVar]: - ... # pragma: no cover +) -> contextlib._GeneratorContextManager[IntVar]: ... # pragma: no cover @overload @@ -95,8 +94,7 @@ def ForIn( iterator: Iterable[AstConvertible] | range | AstConvertible, identifier_name: Optional[str], identifier_type: type[ClassicalVarT], -) -> contextlib._GeneratorContextManager[ClassicalVarT]: - ... # pragma: no cover +) -> contextlib._GeneratorContextManager[ClassicalVarT]: ... # pragma: no cover @contextlib.contextmanager diff --git a/oqpy/program.py b/oqpy/program.py index 6857032..acdc9a9 100644 --- a/oqpy/program.py +++ b/oqpy/program.py @@ -456,10 +456,7 @@ def delay_all( time: AstConvertible, ) -> Program: """Apply a delay to all qubits or frames.""" - ast_duration = to_ast(self, convert_float_to_duration(time, require_nonnegative=True)) - - self._add_statement(ast.DelayInstruction(ast_duration, [])) - return self + return self.delay(None, time) def barrier(self, qubits_or_frames: Iterable[AstConvertible] | None = None) -> Program: """Apply a barrier to a set of qubits or frames."""