diff --git a/pyqir/pyqir/_basicqis.py b/pyqir/pyqir/_basicqis.py index 4cc8234d..042ba6ba 100644 --- a/pyqir/pyqir/_basicqis.py +++ b/pyqir/pyqir/_basicqis.py @@ -60,6 +60,15 @@ def reset(self, qubit: Value) -> None: """ qis.reset(self._builder, qubit) + def delay(self, duration: Union[Value, float], qubit: Value) -> None: + """ + Inserts a delay operation. + + :param duration: The duration the qubit needs to wait for. + :param qubit: The qubit to make wait. + """ + qis.delay(self._builder, duration, qubit) + def rx(self, theta: Union[Value, float], qubit: Value) -> None: """ Inserts a rotation gate about the :math:`x` axis. diff --git a/pyqir/pyqir/_native.pyi b/pyqir/pyqir/_native.pyi index 0c84966a..0470f1b9 100644 --- a/pyqir/pyqir/_native.pyi +++ b/pyqir/pyqir/_native.pyi @@ -1071,6 +1071,15 @@ def reset(builder: Builder, qubit: Value) -> None: """ ... +def delay(builder: Builder, duration: Union[Value, float], qubit: Value) -> None: + """ + Inserts a delay operation. + + :param builder: The underlying builder used to build QIS instructions. + :param duration: The duration the qubit needs to wait for. + :param qubit: The qubit to make wait. + """ + def rx(builder: Builder, theta: Union[Value, float], qubit: Value) -> None: """ Inserts a rotation gate about the :math:`x` axis. diff --git a/pyqir/pyqir/qis.py b/pyqir/pyqir/qis.py index 70aa16fb..7913fb60 100644 --- a/pyqir/pyqir/qis.py +++ b/pyqir/pyqir/qis.py @@ -9,6 +9,7 @@ h, mz, reset, + delay, rx, ry, rz, @@ -31,6 +32,7 @@ "h", "mz", "reset", + "delay", "rx", "ry", "rz", diff --git a/pyqir/src/python.rs b/pyqir/src/python.rs index a594d69a..5bf450af 100644 --- a/pyqir/src/python.rs +++ b/pyqir/src/python.rs @@ -10,8 +10,8 @@ use crate::{ metadata::{ConstantAsMetadata, Metadata, MetadataString}, module::{Linkage, Module, ModuleFlagBehavior}, qis::{ - barrier, ccx, cx, cz, h, if_result, mz, reset, rx, ry, rz, s, s_adj, swap, t, t_adj, x, y, - z, + barrier, ccx, cx, cz, delay, h, if_result, mz, reset, rx, ry, rz, s, s_adj, swap, t, t_adj, + x, y, z, }, rt::{array_record_output, initialize, result_record_output, tuple_record_output}, types::{ @@ -93,6 +93,7 @@ fn _native(_py: Python, m: &PyModule) -> PyResult<()> { m.add_function(wrap_pyfunction!(h, m)?)?; m.add_function(wrap_pyfunction!(mz, m)?)?; m.add_function(wrap_pyfunction!(reset, m)?)?; + m.add_function(wrap_pyfunction!(delay, m)?)?; m.add_function(wrap_pyfunction!(rx, m)?)?; m.add_function(wrap_pyfunction!(ry, m)?)?; m.add_function(wrap_pyfunction!(rz, m)?)?; diff --git a/pyqir/src/qis.rs b/pyqir/src/qis.rs index 31504dfb..aef83139 100644 --- a/pyqir/src/qis.rs +++ b/pyqir/src/qis.rs @@ -156,6 +156,39 @@ pub(crate) fn reset(py: Python, builder: &Builder, qubit: &Value) -> PyResult<() Ok(()) } +/// Inserts a delay operation. +/// +/// :param Builder builder: The IR Builder used to create the instructions +/// :param typing.Union[Value, float] duration: The duration the qubit needs to wait for. +/// :param Value qubit: The qubit to make wait. +/// :rtype: None +#[pyfunction] +#[pyo3(text_signature = "(builder, duration, qubit)")] +pub(crate) fn delay( + py: Python, + builder: &Builder, + duration: Double, + qubit: &Value, +) -> PyResult<()> { + Owner::merge( + py, + [Some(builder.owner()), duration.owner(), Some(qubit.owner())] + .into_iter() + .flatten(), + )?; + + let context = builder.owner().context(py); + let context = context.borrow(py); + unsafe { + qis::build_delay( + builder.as_ptr(), + duration.to_value(context.as_ptr()), + qubit.as_ptr(), + ); + } + Ok(()) +} + /// Inserts a rotation gate about the :math:`x` axis. /// /// :param Builder builder: The IR Builder used to create the instructions @@ -164,7 +197,7 @@ pub(crate) fn reset(py: Python, builder: &Builder, qubit: &Value) -> PyResult<() /// :rtype: None #[pyfunction] #[pyo3(text_signature = "(builder, theta, qubit)")] -pub(crate) fn rx(py: Python, builder: &Builder, theta: Angle, qubit: &Value) -> PyResult<()> { +pub(crate) fn rx(py: Python, builder: &Builder, theta: Double, qubit: &Value) -> PyResult<()> { Owner::merge( py, [Some(builder.owner()), theta.owner(), Some(qubit.owner())] @@ -192,7 +225,7 @@ pub(crate) fn rx(py: Python, builder: &Builder, theta: Angle, qubit: &Value) -> /// :rtype: None #[pyfunction] #[pyo3(text_signature = "(builder, theta, qubit)")] -pub(crate) fn ry(py: Python, builder: &Builder, theta: Angle, qubit: &Value) -> PyResult<()> { +pub(crate) fn ry(py: Python, builder: &Builder, theta: Double, qubit: &Value) -> PyResult<()> { Owner::merge( py, [Some(builder.owner()), theta.owner(), Some(qubit.owner())] @@ -220,7 +253,7 @@ pub(crate) fn ry(py: Python, builder: &Builder, theta: Angle, qubit: &Value) -> /// :rtype: None #[pyfunction] #[pyo3(text_signature = "(builder, theta, qubit)")] -pub(crate) fn rz(py: Python, builder: &Builder, theta: Angle, qubit: &Value) -> PyResult<()> { +pub(crate) fn rz(py: Python, builder: &Builder, theta: Double, qubit: &Value) -> PyResult<()> { Owner::merge( py, [Some(builder.owner()), theta.owner(), Some(qubit.owner())] @@ -379,23 +412,23 @@ pub(crate) fn if_result( } #[derive(FromPyObject)] -pub(crate) enum Angle<'py> { +pub(crate) enum Double<'py> { Value(PyRef<'py, Value>), Constant(f64), } -impl Angle<'_> { +impl Double<'_> { fn owner(&self) -> Option<&Owner> { match self { - Angle::Value(v) => Some(v.owner()), - Angle::Constant(_) => None, + Double::Value(v) => Some(v.owner()), + Double::Constant(_) => None, } } unsafe fn to_value(&self, context: LLVMContextRef) -> LLVMValueRef { match self { - Angle::Value(v) => v.as_ptr(), - &Angle::Constant(c) => LLVMConstReal(LLVMDoubleTypeInContext(context), c), + Double::Value(v) => v.as_ptr(), + &Double::Constant(c) => LLVMConstReal(LLVMDoubleTypeInContext(context), c), } } } diff --git a/pyqir/tests/test_generator.py b/pyqir/tests/test_generator.py index a4295470..8121ff11 100644 --- a/pyqir/tests/test_generator.py +++ b/pyqir/tests/test_generator.py @@ -98,6 +98,7 @@ def test_all_gates() -> None: qis.cz(q[1], control) qis.h(q[0]) qis.reset(q[0]) + qis.delay(14.0, q[0]) qis.rx(15.0, q[1]) qis.ry(16.0, q[2]) qis.rz(17.0, q[3]) diff --git a/pyqir/tests/test_qis.py b/pyqir/tests/test_qis.py index d96c58a7..8f16c8f2 100644 --- a/pyqir/tests/test_qis.py +++ b/pyqir/tests/test_qis.py @@ -118,6 +118,7 @@ def test_adjoint( @pytest.mark.parametrize( "name, get_gate", [ + ("delay", lambda qis: qis.delay), ("rx", lambda qis: qis.rx), ("ry", lambda qis: qis.ry), ("rz", lambda qis: qis.rz), @@ -130,7 +131,7 @@ def test_adjoint( lambda _: 1.0, ], ) -def test_rotated( +def test_double_param_gates( name: str, get_gate: Callable[[BasicQisBuilder], Callable[[Union[Value, float], Value], None]], get_value: Callable[[Context], Union[Value, float]], diff --git a/qirlib/resources/tests/qis/delay.ll b/qirlib/resources/tests/qis/delay.ll new file mode 100644 index 00000000..9bf96b76 --- /dev/null +++ b/qirlib/resources/tests/qis/delay.ll @@ -0,0 +1,13 @@ +; ModuleID = 'delay' +source_filename = "delay" + +%Qubit = type opaque + +define void @main() #0 { + call void @__quantum__qis__delay__body(double 0.000000e+00, %Qubit* null) + ret void +} + +declare void @__quantum__qis__delay__body(double, %Qubit*) + +attributes #0 = { "entry_point" "num_required_qubits"="1" "num_required_results"="0" "output_labeling_schema" "qir_profiles"="custom" } diff --git a/qirlib/src/qis.rs b/qirlib/src/qis.rs index 83e7e7fe..49a97f68 100644 --- a/qirlib/src/qis.rs +++ b/qirlib/src/qis.rs @@ -5,8 +5,8 @@ use crate::{ builder::{build_if, try_build_if}, types, utils::{ - build_call, builder_module, controlled_gate, declare_qis, doubly_controlled_gate, - function_type, no_param, rotation_gate, simple_gate, two_qubit_gate, Functor, + build_call, builder_module, controlled_gate, declare_qis, double_param_gate, + doubly_controlled_gate, function_type, no_param, simple_gate, two_qubit_gate, Functor, }, }; @@ -127,7 +127,7 @@ pub unsafe fn build_z(builder: LLVMBuilderRef, qubit: LLVMValueRef) { pub unsafe fn build_rx(builder: LLVMBuilderRef, theta: LLVMValueRef, qubit: LLVMValueRef) { build_call( builder, - rotation_gate(builder_module(builder), "rx"), + double_param_gate(builder_module(builder), "rx"), &mut [theta, qubit], ); } @@ -135,7 +135,7 @@ pub unsafe fn build_rx(builder: LLVMBuilderRef, theta: LLVMValueRef, qubit: LLVM pub unsafe fn build_ry(builder: LLVMBuilderRef, theta: LLVMValueRef, qubit: LLVMValueRef) { build_call( builder, - rotation_gate(builder_module(builder), "ry"), + double_param_gate(builder_module(builder), "ry"), &mut [theta, qubit], ); } @@ -143,7 +143,7 @@ pub unsafe fn build_ry(builder: LLVMBuilderRef, theta: LLVMValueRef, qubit: LLVM pub unsafe fn build_rz(builder: LLVMBuilderRef, theta: LLVMValueRef, qubit: LLVMValueRef) { build_call( builder, - rotation_gate(builder_module(builder), "rz"), + double_param_gate(builder_module(builder), "rz"), &mut [theta, qubit], ); } @@ -156,6 +156,14 @@ pub unsafe fn build_reset(builder: LLVMBuilderRef, qubit: LLVMValueRef) { ); } +pub unsafe fn build_delay(builder: LLVMBuilderRef, theta: LLVMValueRef, qubit: LLVMValueRef) { + build_call( + builder, + double_param_gate(builder_module(builder), "delay"), + &mut [theta, qubit], + ); +} + pub unsafe fn build_mz(builder: LLVMBuilderRef, qubit: LLVMValueRef, result: LLVMValueRef) { build_call(builder, mz(builder_module(builder)), &mut [qubit, result]); } @@ -391,6 +399,15 @@ mod tests { }); } + #[test] + fn delay() { + assert_reference_ir("qis/delay", 1, 0, |builder| unsafe { + let context = builder_context(builder).unwrap().as_ptr(); + let double = LLVMDoubleTypeInContext(context); + build_delay(builder, LLVMConstReal(double, 0.0), qubit(context, 0)); + }); + } + #[test] fn mz() { assert_reference_ir("qis/mz", 1, 1, |builder| unsafe { diff --git a/qirlib/src/utils.rs b/qirlib/src/utils.rs index 80b21245..2a9f2569 100644 --- a/qirlib/src/utils.rs +++ b/qirlib/src/utils.rs @@ -77,7 +77,7 @@ pub(crate) unsafe fn doubly_controlled_gate(module: LLVMModuleRef, name: &str) - declare_qis(module, name, Functor::Body, ty) } -pub(crate) unsafe fn rotation_gate(module: LLVMModuleRef, name: &str) -> LLVMValueRef { +pub(crate) unsafe fn double_param_gate(module: LLVMModuleRef, name: &str) -> LLVMValueRef { let context = LLVMGetModuleContext(module); let ty = function_type( LLVMVoidTypeInContext(context),