diff --git a/compiler/qsc_ast/src/ast.rs b/compiler/qsc_ast/src/ast.rs index 95a28f89f7..cca6b78e2b 100644 --- a/compiler/qsc_ast/src/ast.rs +++ b/compiler/qsc_ast/src/ast.rs @@ -7,7 +7,7 @@ use indenter::{indented, Format, Indented}; use num_bigint::BigInt; -use qsc_data_structures::span::Span; +use qsc_data_structures::span::{Span, WithSpan}; use std::{ cmp::Ordering, fmt::{self, Display, Formatter, Write}, @@ -107,6 +107,22 @@ impl Hash for NodeId { } } +/// Trait that allows creation of a default value with a span. +pub trait DefaultWithSpan { + /// Creates a default value with the given span by using the `Default` and `WithSpan` traits. + #[must_use] + fn default_with_span(span: Span) -> Self; +} + +impl DefaultWithSpan for Box +where + T: Default + WithSpan, +{ + fn default_with_span(span: Span) -> Self { + Box::new(T::default().with_span(span)) + } +} + /// The root node of an AST. #[derive(Clone, Debug, Default, PartialEq)] pub struct Package { @@ -317,7 +333,7 @@ impl Display for Attr { } /// A type definition. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Default)] pub struct TyDef { /// The node ID. pub id: NodeId, @@ -333,8 +349,14 @@ impl Display for TyDef { } } +impl WithSpan for TyDef { + fn with_span(self, span: Span) -> Self { + Self { span, ..self } + } +} + /// A type definition kind. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Default)] pub enum TyDefKind { /// A field definition with an optional name but required type. Field(Option>, Box), @@ -342,6 +364,9 @@ pub enum TyDefKind { Paren(Box), /// A tuple. Tuple(Box<[Box]>), + /// An invalid type definition. + #[default] + Err, } impl Display for TyDefKind { @@ -372,6 +397,7 @@ impl Display for TyDefKind { } } } + TyDefKind::Err => write!(indent, "Err")?, } Ok(()) } @@ -541,7 +567,7 @@ impl Display for FunctorExprKind { } /// A type. -#[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Debug, Eq, Hash, PartialEq, Default)] pub struct Ty { /// The node ID. pub id: NodeId, @@ -557,8 +583,22 @@ impl Display for Ty { } } +impl WithSpan for Ty { + fn with_span(self, span: Span) -> Self { + Self { span, ..self } + } +} + +impl DefaultWithSpan for Ty { + /// Creates a default value with the given span by using the `Default` and `WithSpan` traits. + #[must_use] + fn default_with_span(span: Span) -> Self { + Self::default().with_span(span) + } +} + /// A type kind. -#[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Debug, Eq, Hash, PartialEq, Default)] pub enum TyKind { /// An array type. Array(Box), @@ -574,6 +614,9 @@ pub enum TyKind { Param(Box), /// A tuple type. Tuple(Box<[Ty]>), + /// An invalid type. + #[default] + Err, } impl Display for TyKind { @@ -607,6 +650,7 @@ impl Display for TyKind { } } } + TyKind::Err => write!(indent, "Err")?, } Ok(()) } @@ -722,6 +766,12 @@ impl Display for Expr { } } +impl WithSpan for Expr { + fn with_span(self, span: Span) -> Self { + Self { span, ..self } + } +} + /// An expression kind. #[derive(Clone, Debug, Default, PartialEq)] pub enum ExprKind { @@ -1082,7 +1132,7 @@ pub enum StringComponent { } /// A pattern. -#[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Debug, Eq, Hash, PartialEq, Default)] pub struct Pat { /// The node ID. pub id: NodeId, @@ -1098,8 +1148,14 @@ impl Display for Pat { } } +impl WithSpan for Pat { + fn with_span(self, span: Span) -> Self { + Self { span, ..self } + } +} + /// A pattern kind. -#[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Debug, Eq, Hash, PartialEq, Default)] pub enum PatKind { /// A binding with an optional type annotation. Bind(Box, Option>), @@ -1111,6 +1167,9 @@ pub enum PatKind { Paren(Box), /// A tuple: `(a, b, c)`. Tuple(Box<[Box]>), + /// An invalid pattern. + #[default] + Err, } impl Display for PatKind { @@ -1150,13 +1209,14 @@ impl Display for PatKind { } } } + PatKind::Err => write!(indent, "Err")?, } Ok(()) } } /// A qubit initializer. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Default)] pub struct QubitInit { /// The node ID. pub id: NodeId, @@ -1172,8 +1232,14 @@ impl Display for QubitInit { } } +impl WithSpan for QubitInit { + fn with_span(self, span: Span) -> Self { + Self { span, ..self } + } +} + /// A qubit initializer kind. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Default)] pub enum QubitInitKind { /// An array of qubits: `Qubit[a]`. Array(Box), @@ -1183,6 +1249,9 @@ pub enum QubitInitKind { Single, /// A tuple: `(a, b, c)`. Tuple(Box<[Box]>), + /// An invalid initializer. + #[default] + Err, } impl Display for QubitInitKind { @@ -1211,13 +1280,14 @@ impl Display for QubitInitKind { } } } + QubitInitKind::Err => write!(indent, "Err")?, } Ok(()) } } /// A path to a declaration. -#[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Debug, Eq, Hash, PartialEq, Default)] pub struct Path { /// The node ID. pub id: NodeId, @@ -1240,6 +1310,12 @@ impl Display for Path { } } +impl WithSpan for Path { + fn with_span(self, span: Span) -> Self { + Self { span, ..self } + } +} + /// An identifier. #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct Ident { @@ -1251,6 +1327,22 @@ pub struct Ident { pub name: Rc, } +impl Default for Ident { + fn default() -> Self { + Ident { + id: NodeId::default(), + span: Span::default(), + name: "".into(), + } + } +} + +impl WithSpan for Ident { + fn with_span(self, span: Span) -> Self { + Self { span, ..self } + } +} + impl Display for Ident { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "Ident {} {} \"{}\"", self.id, self.span, self.name) diff --git a/compiler/qsc_ast/src/mut_visit.rs b/compiler/qsc_ast/src/mut_visit.rs index b1a445aea1..8b47a31bb4 100644 --- a/compiler/qsc_ast/src/mut_visit.rs +++ b/compiler/qsc_ast/src/mut_visit.rs @@ -130,6 +130,7 @@ pub fn walk_ty_def(vis: &mut impl MutVisitor, def: &mut TyDef) { } TyDefKind::Paren(def) => vis.visit_ty_def(def), TyDefKind::Tuple(defs) => defs.iter_mut().for_each(|d| vis.visit_ty_def(d)), + TyDefKind::Err => {} } } @@ -184,7 +185,7 @@ pub fn walk_ty(vis: &mut impl MutVisitor, ty: &mut Ty) { vis.visit_ty(rhs); functors.iter_mut().for_each(|f| vis.visit_functor_expr(f)); } - TyKind::Hole => {} + TyKind::Hole | TyKind::Err => {} TyKind::Paren(ty) => vis.visit_ty(ty), TyKind::Param(name) => vis.visit_ident(name), TyKind::Path(path) => vis.visit_path(path), @@ -313,7 +314,7 @@ pub fn walk_pat(vis: &mut impl MutVisitor, pat: &mut Pat) { ty.iter_mut().for_each(|t| vis.visit_ty(t)); } PatKind::Discard(ty) => ty.iter_mut().for_each(|t| vis.visit_ty(t)), - PatKind::Elided => {} + PatKind::Elided | PatKind::Err => {} PatKind::Paren(pat) => vis.visit_pat(pat), PatKind::Tuple(pats) => pats.iter_mut().for_each(|p| vis.visit_pat(p)), } @@ -325,7 +326,7 @@ pub fn walk_qubit_init(vis: &mut impl MutVisitor, init: &mut QubitInit) { match &mut *init.kind { QubitInitKind::Array(len) => vis.visit_expr(len), QubitInitKind::Paren(init) => vis.visit_qubit_init(init), - QubitInitKind::Single => {} + QubitInitKind::Single | QubitInitKind::Err => {} QubitInitKind::Tuple(inits) => inits.iter_mut().for_each(|i| vis.visit_qubit_init(i)), } } diff --git a/compiler/qsc_ast/src/visit.rs b/compiler/qsc_ast/src/visit.rs index d80b09cacc..f9afe8b098 100644 --- a/compiler/qsc_ast/src/visit.rs +++ b/compiler/qsc_ast/src/visit.rs @@ -117,6 +117,7 @@ pub fn walk_ty_def<'a>(vis: &mut impl Visitor<'a>, def: &'a TyDef) { } TyDefKind::Paren(def) => vis.visit_ty_def(def), TyDefKind::Tuple(defs) => defs.iter().for_each(|d| vis.visit_ty_def(d)), + TyDefKind::Err => {} } } @@ -161,7 +162,7 @@ pub fn walk_ty<'a>(vis: &mut impl Visitor<'a>, ty: &'a Ty) { vis.visit_ty(rhs); functors.iter().for_each(|f| vis.visit_functor_expr(f)); } - TyKind::Hole => {} + TyKind::Hole | TyKind::Err => {} TyKind::Paren(ty) => vis.visit_ty(ty), TyKind::Path(path) => vis.visit_path(path), TyKind::Param(name) => vis.visit_ident(name), @@ -283,7 +284,7 @@ pub fn walk_pat<'a>(vis: &mut impl Visitor<'a>, pat: &'a Pat) { ty.iter().for_each(|t| vis.visit_ty(t)); } PatKind::Discard(ty) => ty.iter().for_each(|t| vis.visit_ty(t)), - PatKind::Elided => {} + PatKind::Elided | PatKind::Err => {} PatKind::Paren(pat) => vis.visit_pat(pat), PatKind::Tuple(pats) => pats.iter().for_each(|p| vis.visit_pat(p)), } @@ -293,7 +294,7 @@ pub fn walk_qubit_init<'a>(vis: &mut impl Visitor<'a>, init: &'a QubitInit) { match &*init.kind { QubitInitKind::Array(len) => vis.visit_expr(len), QubitInitKind::Paren(init) => vis.visit_qubit_init(init), - QubitInitKind::Single => {} + QubitInitKind::Single | QubitInitKind::Err => {} QubitInitKind::Tuple(inits) => inits.iter().for_each(|i| vis.visit_qubit_init(i)), } } diff --git a/compiler/qsc_codegen/src/qir_base.rs b/compiler/qsc_codegen/src/qir_base.rs index 72ec3269ad..d381c7b2b4 100644 --- a/compiler/qsc_codegen/src/qir_base.rs +++ b/compiler/qsc_codegen/src/qir_base.rs @@ -121,6 +121,7 @@ pub(super) fn get_global( pub struct BaseProfSim { next_meas_id: usize, next_qubit_id: usize, + next_qubit_hardware_id: usize, qubit_map: IndexMap, instrs: String, measurements: String, @@ -138,6 +139,7 @@ impl BaseProfSim { let mut sim = BaseProfSim { next_meas_id: 0, next_qubit_id: 0, + next_qubit_hardware_id: 0, qubit_map: IndexMap::new(), instrs: String::new(), measurements: String::new(), @@ -155,7 +157,7 @@ impl BaseProfSim { write!( self.instrs, include_str!("./qir_base/postfix.ll"), - self.next_qubit_id, self.next_meas_id + self.next_qubit_hardware_id, self.next_meas_id ) .expect("writing to string should succeed"); @@ -173,8 +175,8 @@ impl BaseProfSim { if let Some(mapped) = self.qubit_map.get(qubit) { *mapped } else { - let mapped = self.next_qubit_id; - self.next_qubit_id += 1; + let mapped = self.next_qubit_hardware_id; + self.next_qubit_hardware_id += 1; self.qubit_map.insert(qubit, mapped); mapped } @@ -301,13 +303,12 @@ impl Backend for BaseProfSim { Result(id), ) .expect("writing to string should succeed"); + self.reset(q); id } fn mresetz(&mut self, q: usize) -> Self::ResultType { - let id = self.m(q); - self.reset(q); - id + self.m(q) } fn reset(&mut self, q: usize) { @@ -474,12 +475,12 @@ impl Backend for BaseProfSim { fn qubit_allocate(&mut self) -> usize { let id = self.next_qubit_id; self.next_qubit_id += 1; - self.qubit_map.insert(id, id); + let _ = self.map(id); id } fn qubit_release(&mut self, _q: usize) { - // Base Profile qubits are never released, since they cannot be reused. + self.next_qubit_id -= 1; } fn capture_quantum_state(&mut self) -> (Vec<(BigUint, Complex)>, usize) { diff --git a/compiler/qsc_codegen/src/qir_base/tests.rs b/compiler/qsc_codegen/src/qir_base/tests.rs index 6ef34642ed..2030de25a9 100644 --- a/compiler/qsc_codegen/src/qir_base/tests.rs +++ b/compiler/qsc_codegen/src/qir_base/tests.rs @@ -305,6 +305,189 @@ fn output_recording_tuple() { ); } +#[test] +fn reset_allocates_new_qubit_id() { + check( + "", + Some(indoc! {"{use q = Qubit(); H(q); Reset(q); H(q); M(q)}"}), + &expect![[r#" + %Result = type opaque + %Qubit = type opaque + + define void @ENTRYPOINT__main() #0 { + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) + call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) #1 + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null) + ret void + } + + declare void @__quantum__qis__ccx__body(%Qubit*, %Qubit*, %Qubit*) + declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*) + declare void @__quantum__qis__cy__body(%Qubit*, %Qubit*) + declare void @__quantum__qis__cz__body(%Qubit*, %Qubit*) + declare void @__quantum__qis__rx__body(double, %Qubit*) + declare void @__quantum__qis__rxx__body(double, %Qubit*, %Qubit*) + declare void @__quantum__qis__ry__body(double, %Qubit*) + declare void @__quantum__qis__ryy__body(double, %Qubit*, %Qubit*) + declare void @__quantum__qis__rz__body(double, %Qubit*) + declare void @__quantum__qis__rzz__body(double, %Qubit*, %Qubit*) + declare void @__quantum__qis__h__body(%Qubit*) + declare void @__quantum__qis__s__body(%Qubit*) + declare void @__quantum__qis__s__adj(%Qubit*) + declare void @__quantum__qis__t__body(%Qubit*) + declare void @__quantum__qis__t__adj(%Qubit*) + declare void @__quantum__qis__x__body(%Qubit*) + declare void @__quantum__qis__y__body(%Qubit*) + declare void @__quantum__qis__z__body(%Qubit*) + declare void @__quantum__qis__swap__body(%Qubit*, %Qubit*) + declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1 + declare void @__quantum__rt__result_record_output(%Result*, i8*) + declare void @__quantum__rt__array_record_output(i64, i8*) + declare void @__quantum__rt__tuple_record_output(i64, i8*) + + attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="3" "required_num_results"="1" } + attributes #1 = { "irreversible" } + + ; module flags + + !llvm.module.flags = !{!0, !1, !2, !3} + + !0 = !{i32 1, !"qir_major_version", i32 1} + !1 = !{i32 7, !"qir_minor_version", i32 0} + !2 = !{i32 1, !"dynamic_qubit_management", i1 false} + !3 = !{i32 1, !"dynamic_result_management", i1 false} + "#]], + ); +} + +#[test] +fn reuse_after_measurement_uses_fresh_aux_qubit_id() { + check( + "", + Some(indoc! {"{use q = Qubit(); H(q); M(q); H(q); M(q)}"}), + &expect![[r#" + %Result = type opaque + %Qubit = type opaque + + define void @ENTRYPOINT__main() #0 { + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) + call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) #1 + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) #1 + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null) + ret void + } + + declare void @__quantum__qis__ccx__body(%Qubit*, %Qubit*, %Qubit*) + declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*) + declare void @__quantum__qis__cy__body(%Qubit*, %Qubit*) + declare void @__quantum__qis__cz__body(%Qubit*, %Qubit*) + declare void @__quantum__qis__rx__body(double, %Qubit*) + declare void @__quantum__qis__rxx__body(double, %Qubit*, %Qubit*) + declare void @__quantum__qis__ry__body(double, %Qubit*) + declare void @__quantum__qis__ryy__body(double, %Qubit*, %Qubit*) + declare void @__quantum__qis__rz__body(double, %Qubit*) + declare void @__quantum__qis__rzz__body(double, %Qubit*, %Qubit*) + declare void @__quantum__qis__h__body(%Qubit*) + declare void @__quantum__qis__s__body(%Qubit*) + declare void @__quantum__qis__s__adj(%Qubit*) + declare void @__quantum__qis__t__body(%Qubit*) + declare void @__quantum__qis__t__adj(%Qubit*) + declare void @__quantum__qis__x__body(%Qubit*) + declare void @__quantum__qis__y__body(%Qubit*) + declare void @__quantum__qis__z__body(%Qubit*) + declare void @__quantum__qis__swap__body(%Qubit*, %Qubit*) + declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1 + declare void @__quantum__rt__result_record_output(%Result*, i8*) + declare void @__quantum__rt__array_record_output(i64, i8*) + declare void @__quantum__rt__tuple_record_output(i64, i8*) + + attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="3" "required_num_results"="2" } + attributes #1 = { "irreversible" } + + ; module flags + + !llvm.module.flags = !{!0, !1, !2, !3} + + !0 = !{i32 1, !"qir_major_version", i32 1} + !1 = !{i32 7, !"qir_minor_version", i32 0} + !2 = !{i32 1, !"dynamic_qubit_management", i1 false} + !3 = !{i32 1, !"dynamic_result_management", i1 false} + "#]], + ); +} + +#[test] +fn qubit_allocation_allows_reuse_of_unmeasured_qubits() { + check( + "", + Some(indoc! {"{ + open Microsoft.Quantum.Measurement; + { use (c, q) = (Qubit(), Qubit()); CNOT(c, q); MResetZ(q); } + { use (c, q) = (Qubit(), Qubit()); CNOT(c, q); MResetZ(q) } + }"}), + &expect![[r#" + %Result = type opaque + %Qubit = type opaque + + define void @ENTRYPOINT__main() #0 { + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) #1 + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) #1 + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null) + ret void + } + + declare void @__quantum__qis__ccx__body(%Qubit*, %Qubit*, %Qubit*) + declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*) + declare void @__quantum__qis__cy__body(%Qubit*, %Qubit*) + declare void @__quantum__qis__cz__body(%Qubit*, %Qubit*) + declare void @__quantum__qis__rx__body(double, %Qubit*) + declare void @__quantum__qis__rxx__body(double, %Qubit*, %Qubit*) + declare void @__quantum__qis__ry__body(double, %Qubit*) + declare void @__quantum__qis__ryy__body(double, %Qubit*, %Qubit*) + declare void @__quantum__qis__rz__body(double, %Qubit*) + declare void @__quantum__qis__rzz__body(double, %Qubit*, %Qubit*) + declare void @__quantum__qis__h__body(%Qubit*) + declare void @__quantum__qis__s__body(%Qubit*) + declare void @__quantum__qis__s__adj(%Qubit*) + declare void @__quantum__qis__t__body(%Qubit*) + declare void @__quantum__qis__t__adj(%Qubit*) + declare void @__quantum__qis__x__body(%Qubit*) + declare void @__quantum__qis__y__body(%Qubit*) + declare void @__quantum__qis__z__body(%Qubit*) + declare void @__quantum__qis__swap__body(%Qubit*, %Qubit*) + declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1 + declare void @__quantum__rt__result_record_output(%Result*, i8*) + declare void @__quantum__rt__array_record_output(i64, i8*) + declare void @__quantum__rt__tuple_record_output(i64, i8*) + + attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="3" "required_num_results"="2" } + attributes #1 = { "irreversible" } + + ; module flags + + !llvm.module.flags = !{!0, !1, !2, !3} + + !0 = !{i32 1, !"qir_major_version", i32 1} + !1 = !{i32 7, !"qir_minor_version", i32 0} + !2 = !{i32 1, !"dynamic_qubit_management", i1 false} + !3 = !{i32 1, !"dynamic_result_management", i1 false} + "#]], + ); +} + #[test] fn verify_all_intrinsics() { check( @@ -499,158 +682,158 @@ fn complex_program_is_valid() { call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 8 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 8 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 8 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 8 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 8 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 8 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 9 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 9 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 8 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 8 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 6 to %Qubit*)) call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 9 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 9 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 8 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 8 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 9 to %Qubit*), %Qubit* inttoptr (i64 8 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 9 to %Qubit*)) + call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 7 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 5 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 4 to %Qubit*)) call void @__quantum__qis__s__body(%Qubit* inttoptr (i64 4 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 4 to %Qubit*)) call void @__quantum__qis__rz__body(double -0.7853981633974483, %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 9 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) call void @__quantum__qis__rz__body(double 0.7853981633974483, %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 9 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 4 to %Qubit*)) call void @__quantum__qis__s__adj(%Qubit* inttoptr (i64 4 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 4 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 5 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 9 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 9 to %Qubit*), %Qubit* inttoptr (i64 8 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 8 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 8 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 9 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 9 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) + call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 7 to %Qubit*)) call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 8 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 8 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 9 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 9 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 8 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 8 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) + call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 8 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 8 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 6 to %Qubit*)) call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 8 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 8 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 10 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 10 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 10 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 10 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 10 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 10 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 11 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 11 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 10 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 10 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 6 to %Qubit*)) call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 11 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 11 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 10 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 10 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 11 to %Qubit*), %Qubit* inttoptr (i64 10 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 11 to %Qubit*)) + call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 7 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 5 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) call void @__quantum__qis__rz__body(double -0.7853981633974483, %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 11 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) call void @__quantum__qis__rz__body(double 0.7853981633974483, %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 11 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 5 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 11 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 11 to %Qubit*), %Qubit* inttoptr (i64 10 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 10 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 10 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 11 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 11 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) + call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 7 to %Qubit*)) call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 10 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 10 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 11 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 11 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 10 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 10 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) + call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 10 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 10 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 6 to %Qubit*)) call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 10 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 10 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 4 to %Qubit*), %Qubit* inttoptr (i64 5 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 12 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 12 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 12 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 12 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 12 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 12 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 13 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 13 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 5 to %Qubit*)) call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 5 to %Qubit*)) call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 13 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 13 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) + call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 7 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 5 to %Qubit*)) call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 5 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 13 to %Qubit*), %Qubit* inttoptr (i64 5 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 13 to %Qubit*)) - call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 13 to %Qubit*), %Qubit* inttoptr (i64 12 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 13 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 13 to %Qubit*), %Qubit* inttoptr (i64 5 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 5 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) + call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 5 to %Qubit*)) call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 5 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 5 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 13 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 13 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) + call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 7 to %Qubit*)) call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 3 to %Qubit*)) call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 5 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 5 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 13 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 13 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 12 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 12 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 12 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 12 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 6 to %Qubit*)) call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 12 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 12 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 4 to %Qubit*), %Qubit* inttoptr (i64 5 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 5 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) @@ -698,7 +881,7 @@ fn complex_program_is_valid() { declare void @__quantum__rt__array_record_output(i64, i8*) declare void @__quantum__rt__tuple_record_output(i64, i8*) - attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="14" "required_num_results"="6" } + attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="8" "required_num_results"="6" } attributes #1 = { "irreversible" } ; module flags diff --git a/compiler/qsc_data_structures/src/span.rs b/compiler/qsc_data_structures/src/span.rs index 9535d33204..0823d42bb5 100644 --- a/compiler/qsc_data_structures/src/span.rs +++ b/compiler/qsc_data_structures/src/span.rs @@ -74,3 +74,9 @@ impl From for SourceSpan { Self::from((value.lo as usize)..(value.hi as usize)) } } + +#[allow(clippy::module_name_repetitions)] +pub trait WithSpan { + #[must_use] + fn with_span(self, span: Span) -> Self; +} diff --git a/compiler/qsc_eval/src/lower.rs b/compiler/qsc_eval/src/lower.rs index d15e906d9f..e886b3a942 100644 --- a/compiler/qsc_eval/src/lower.rs +++ b/compiler/qsc_eval/src/lower.rs @@ -213,6 +213,7 @@ impl Lowerer { hir::PatKind::Tuple(elems) => { fir::PatKind::Tuple(elems.iter().map(|pat| self.lower_pat(pat)).collect()) } + hir::PatKind::Err => unreachable!("error pat should not be present"), }; let pat = fir::Pat { id, span, ty, kind }; @@ -389,6 +390,7 @@ impl Lowerer { hir::PatKind::Tuple(items) => { fir::PatKind::Tuple(items.iter().map(|i| self.lower_pat(i)).collect()) } + hir::PatKind::Err => unreachable!("error pat should not be present"), }; let pat = fir::Pat { @@ -410,6 +412,7 @@ impl Lowerer { hir::QubitInitKind::Tuple(items) => { fir::QubitInitKind::Tuple(items.iter().map(|i| self.lower_qubit_init(i)).collect()) } + hir::QubitInitKind::Err => unreachable!("error qubit init should not be present"), }; fir::QubitInit { diff --git a/compiler/qsc_frontend/src/lower.rs b/compiler/qsc_frontend/src/lower.rs index 5d6ef351aa..d65fe48231 100644 --- a/compiler/qsc_frontend/src/lower.rs +++ b/compiler/qsc_frontend/src/lower.rs @@ -674,6 +674,7 @@ impl With<'_> { ast::PatKind::Tuple(items) => { hir::PatKind::Tuple(items.iter().map(|i| self.lower_pat(i)).collect()) } + ast::PatKind::Err => hir::PatKind::Err, }; hir::Pat { @@ -700,6 +701,7 @@ impl With<'_> { ast::QubitInitKind::Tuple(items) => { hir::QubitInitKind::Tuple(items.iter().map(|i| self.lower_qubit_init(i)).collect()) } + ast::QubitInitKind::Err => hir::QubitInitKind::Err, }; hir::QubitInit { diff --git a/compiler/qsc_frontend/src/lower/tests.rs b/compiler/qsc_frontend/src/lower/tests.rs index 930fdd66eb..91eb19df67 100644 --- a/compiler/qsc_frontend/src/lower/tests.rs +++ b/compiler/qsc_frontend/src/lower/tests.rs @@ -2030,3 +2030,162 @@ fn lambda_with_invalid_free_variable() { ctl-adj: "#]], ); } + +#[test] +fn duplicate_commas_in_tydef() { + check_hir( + indoc! {r#" + namespace test { + newtype Foo = (Int,,); + } + "#}, + &expect![[r#" + Package: + Item 0 [0-45] (Public): + Namespace (Ident 1 [10-14] "test"): Item 1 + Item 1 [21-43] (Public): + Parent: 0 + Type (Ident 0 [29-32] "Foo"): UDT [21-43]: + TyDef [35-42]: Tuple: + TyDef [36-39]: Field: + type: Int + TyDef [40-40]: Field: + type: ?"#]], + ); +} + +#[test] +fn duplicate_commas_in_generics() { + check_hir( + indoc! {r#" + namespace test { + function Foo<'T,,>(x : 'T) : Unit {} + } + "#}, + &expect![[r#" + Package: + Item 0 [0-59] (Public): + Namespace (Ident 6 [10-14] "test"): Item 1 + Item 1 [21-57] (Public): + Parent: 0 + Callable 0 [21-57] (function): + name: Ident 1 [30-33] "Foo" + generics: + 0: type + 1: type + input: Pat 2 [40-46] [Type 0]: Bind: Ident 3 [40-41] "x" + output: Unit + functors: empty set + body: SpecDecl 4 [21-57]: Impl: + Block 5 [55-57]: + adj: + ctl: + ctl-adj: "#]], + ); +} + +#[test] +fn duplicate_commas_in_pat() { + check_hir( + indoc! {r#" + namespace test { + operation Foo() : Unit { + let (x,,) = (1, 2); + } + }"#}, + &expect![[r#" + Package: + Item 0 [0-81] (Public): + Namespace (Ident 13 [10-14] "test"): Item 1 + Item 1 [21-79] (Public): + Parent: 0 + Callable 0 [21-79] (operation): + name: Ident 1 [31-34] "Foo" + input: Pat 2 [34-36] [Type Unit]: Unit + output: Unit + functors: empty set + body: SpecDecl 3 [21-79]: Impl: + Block 4 [44-79] [Type Unit]: + Stmt 5 [54-73]: Local (Immutable): + Pat 6 [58-63] [Type (Int, ?)]: Tuple: + Pat 7 [59-60] [Type Int]: Bind: Ident 8 [59-60] "x" + Pat 9 [61-61] [Type ?]: Err + Expr 10 [66-72] [Type (Int, Int)]: Tuple: + Expr 11 [67-68] [Type Int]: Lit: Int(1) + Expr 12 [70-71] [Type Int]: Lit: Int(2) + adj: + ctl: + ctl-adj: "#]], + ); +} + +#[test] +fn duplicate_commas_in_tuple() { + check_hir( + indoc! {r#" + namespace test { + operation Foo() : Unit { + let x = (1,,3); + } + }"#}, + &expect![[r#" + Package: + Item 0 [0-77] (Public): + Namespace (Ident 12 [10-14] "test"): Item 1 + Item 1 [21-75] (Public): + Parent: 0 + Callable 0 [21-75] (operation): + name: Ident 1 [31-34] "Foo" + input: Pat 2 [34-36] [Type Unit]: Unit + output: Unit + functors: empty set + body: SpecDecl 3 [21-75]: Impl: + Block 4 [44-75] [Type Unit]: + Stmt 5 [54-69]: Local (Immutable): + Pat 6 [58-59] [Type (Int, ?, Int)]: Bind: Ident 7 [58-59] "x" + Expr 8 [62-68] [Type (Int, ?, Int)]: Tuple: + Expr 9 [63-64] [Type Int]: Lit: Int(1) + Expr 10 [65-65] [Type ?]: Err + Expr 11 [66-67] [Type Int]: Lit: Int(3) + adj: + ctl: + ctl-adj: "#]], + ); +} + +#[test] +fn duplicate_commas_in_arg_tuple() { + check_hir( + indoc! {r#" + namespace test { + operation Foo(a : Int, b : Int, c : Int) : Int { + Foo(a,,c) + } + }"#}, + &expect![[r#" + Package: + Item 0 [0-95] (Public): + Namespace (Ident 18 [10-14] "test"): Item 1 + Item 1 [21-93] (Public): + Parent: 0 + Callable 0 [21-93] (operation): + name: Ident 1 [31-34] "Foo" + input: Pat 2 [34-61] [Type (Int, Int, Int)]: Tuple: + Pat 3 [35-42] [Type Int]: Bind: Ident 4 [35-36] "a" + Pat 5 [44-51] [Type Int]: Bind: Ident 6 [44-45] "b" + Pat 7 [53-60] [Type Int]: Bind: Ident 8 [53-54] "c" + output: Int + functors: empty set + body: SpecDecl 9 [21-93]: Impl: + Block 10 [68-93] [Type Int]: + Stmt 11 [78-87]: Expr: Expr 12 [78-87] [Type Int]: Call: + Expr 13 [78-81] [Type ((Int, Int, Int) => Int)]: Var: Item 1 + Expr 14 [81-87] [Type (Int, ?, Int)]: Tuple: + Expr 15 [82-83] [Type Int]: Var: Local 4 + Expr 16 [84-84] [Type ?]: Err + Expr 17 [85-86] [Type Int]: Var: Local 8 + adj: + ctl: + ctl-adj: "#]], + ); +} diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 339f5565bd..b1789436d0 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -304,7 +304,7 @@ impl Resolver { self.names.insert(name.id, Res::Local(name.id)); scope.vars.insert(Rc::clone(&name.name), name.id); } - ast::PatKind::Discard(_) | ast::PatKind::Elided => {} + ast::PatKind::Discard(_) | ast::PatKind::Elided | ast::PatKind::Err => {} ast::PatKind::Paren(pat) => self.bind_pat_recursive(pat, bindings), ast::PatKind::Tuple(pats) => pats .iter() @@ -432,7 +432,7 @@ impl AstVisitor<'_> for With<'_> { ast::PatKind::Bind(name, _) => { names.insert(Rc::clone(&name.name)); } - ast::PatKind::Discard(_) | ast::PatKind::Elided => {} + ast::PatKind::Discard(_) | ast::PatKind::Elided | ast::PatKind::Err => {} ast::PatKind::Paren(pat) => collect_param_names(pat, names), ast::PatKind::Tuple(pats) => { pats.iter().for_each(|p| collect_param_names(p, names)); diff --git a/compiler/qsc_frontend/src/typeck/convert.rs b/compiler/qsc_frontend/src/typeck/convert.rs index 0a58c5a987..1c16e034e6 100644 --- a/compiler/qsc_frontend/src/typeck/convert.rs +++ b/compiler/qsc_frontend/src/typeck/convert.rs @@ -75,6 +75,7 @@ pub(crate) fn ty_from_ast(names: &Names, ty: &ast::Ty) -> (Ty, Vec (Ty::Err, Vec::new()), } } @@ -109,6 +110,7 @@ fn ast_ty_def_base(names: &Names, def: &TyDef) -> (Ty, Vec) { (Ty::Tuple(tys), errors) } + TyDefKind::Err => (Ty::Err, Vec::new()), } } @@ -146,6 +148,11 @@ pub(super) fn ast_ty_def(names: &Names, def: &TyDef) -> (UdtDef, Vec UdtDefKind::Field(UdtField { + name_span: None, + name: None, + ty: Ty::Err, + }), }, }; @@ -210,7 +217,7 @@ fn synthesize_functor_params_in_pat( pat: &mut hir::Pat, ) -> Vec { match &mut pat.kind { - hir::PatKind::Discard | hir::PatKind::Bind(_) => { + hir::PatKind::Discard | hir::PatKind::Err | hir::PatKind::Bind(_) => { synthesize_functor_params(next_param, &mut pat.ty) } hir::PatKind::Tuple(items) => { @@ -247,6 +254,7 @@ pub(crate) fn ast_pat_ty(names: &Names, pat: &Pat) -> (Ty, Vec) } (Ty::Tuple(tys), errors) } + PatKind::Err => (Ty::Err, Vec::new()), } } diff --git a/compiler/qsc_frontend/src/typeck/rules.rs b/compiler/qsc_frontend/src/typeck/rules.rs index 6b5bd71238..983be17c21 100644 --- a/compiler/qsc_frontend/src/typeck/rules.rs +++ b/compiler/qsc_frontend/src/typeck/rules.rs @@ -126,6 +126,7 @@ impl<'a> Context<'a> { TyKind::Tuple(items) => { Ty::Tuple(items.iter().map(|item| self.infer_ty(item)).collect()) } + TyKind::Err => Ty::Err, } } @@ -666,6 +667,7 @@ impl<'a> Context<'a> { PatKind::Tuple(items) => { Ty::Tuple(items.iter().map(|item| self.infer_pat(item)).collect()) } + PatKind::Err => Ty::Err, }; self.record(pat.id, ty.clone()); @@ -696,6 +698,7 @@ impl<'a> Context<'a> { } self.diverge_if(diverges, converge(Ty::Tuple(tys))) } + QubitInitKind::Err => converge(Ty::Err), }; self.record(init.id, ty.ty.clone()); diff --git a/compiler/qsc_hir/src/hir.rs b/compiler/qsc_hir/src/hir.rs index 42be2e08d9..949dc6594c 100644 --- a/compiler/qsc_hir/src/hir.rs +++ b/compiler/qsc_hir/src/hir.rs @@ -1037,6 +1037,8 @@ pub enum PatKind { Discard, /// A tuple: `(a, b, c)`. Tuple(Vec), + /// An invalid pattern. + Err, } impl Display for PatKind { @@ -1058,6 +1060,7 @@ impl Display for PatKind { } } } + PatKind::Err => write!(indent, "Err")?, } Ok(()) } @@ -1095,6 +1098,8 @@ pub enum QubitInitKind { Single, /// A tuple: `(a, b, c)`. Tuple(Vec), + /// An invalid qubit initializer. + Err, } impl Display for QubitInitKind { @@ -1118,6 +1123,7 @@ impl Display for QubitInitKind { } } } + QubitInitKind::Err => write!(indent, "Err")?, } Ok(()) } diff --git a/compiler/qsc_hir/src/mut_visit.rs b/compiler/qsc_hir/src/mut_visit.rs index 484214103e..00333b70d3 100644 --- a/compiler/qsc_hir/src/mut_visit.rs +++ b/compiler/qsc_hir/src/mut_visit.rs @@ -210,7 +210,7 @@ pub fn walk_pat(vis: &mut impl MutVisitor, pat: &mut Pat) { match &mut pat.kind { PatKind::Bind(name) => vis.visit_ident(name), - PatKind::Discard => {} + PatKind::Discard | PatKind::Err => {} PatKind::Tuple(pats) => pats.iter_mut().for_each(|p| vis.visit_pat(p)), } } @@ -220,7 +220,7 @@ pub fn walk_qubit_init(vis: &mut impl MutVisitor, init: &mut QubitInit) { match &mut init.kind { QubitInitKind::Array(len) => vis.visit_expr(len), - QubitInitKind::Single => {} + QubitInitKind::Single | QubitInitKind::Err => {} QubitInitKind::Tuple(inits) => inits.iter_mut().for_each(|i| vis.visit_qubit_init(i)), } } diff --git a/compiler/qsc_hir/src/visit.rs b/compiler/qsc_hir/src/visit.rs index f979e38130..6f9655c29a 100644 --- a/compiler/qsc_hir/src/visit.rs +++ b/compiler/qsc_hir/src/visit.rs @@ -189,7 +189,7 @@ pub fn walk_expr<'a>(vis: &mut impl Visitor<'a>, expr: &'a Expr) { pub fn walk_pat<'a>(vis: &mut impl Visitor<'a>, pat: &'a Pat) { match &pat.kind { PatKind::Bind(name) => vis.visit_ident(name), - PatKind::Discard => {} + PatKind::Discard | PatKind::Err => {} PatKind::Tuple(pats) => pats.iter().for_each(|p| vis.visit_pat(p)), } } @@ -197,7 +197,7 @@ pub fn walk_pat<'a>(vis: &mut impl Visitor<'a>, pat: &'a Pat) { pub fn walk_qubit_init<'a>(vis: &mut impl Visitor<'a>, init: &'a QubitInit) { match &init.kind { QubitInitKind::Array(len) => vis.visit_expr(len), - QubitInitKind::Single => {} + QubitInitKind::Single | QubitInitKind::Err => {} QubitInitKind::Tuple(inits) => inits.iter().for_each(|i| vis.visit_qubit_init(i)), } } diff --git a/compiler/qsc_parse/src/expr/tests.rs b/compiler/qsc_parse/src/expr/tests.rs index 0dc826b72b..68d1fa5e0c 100644 --- a/compiler/qsc_parse/src/expr/tests.rs +++ b/compiler/qsc_parse/src/expr/tests.rs @@ -2239,3 +2239,197 @@ fn nested_interpolated_string_with_exprs() { Lit: " baz""#]], ); } + +#[test] +fn duplicate_commas_in_tuple() { + check( + expr, + "(x,, y)", + &expect![[r#" + Expr _id_ [0-7]: Tuple: + Expr _id_ [1-2]: Path: Path _id_ [1-2] (Ident _id_ [1-2] "x") + Expr _id_ [3-3]: Err + Expr _id_ [5-6]: Path: Path _id_ [5-6] (Ident _id_ [5-6] "y") + + [ + Error( + MissingSeqEntry( + Span { + lo: 3, + hi: 3, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn many_duplicate_commas_in_tuple() { + check( + expr, + "(x,,,, y)", + &expect![[r#" + Expr _id_ [0-9]: Tuple: + Expr _id_ [1-2]: Path: Path _id_ [1-2] (Ident _id_ [1-2] "x") + Expr _id_ [3-3]: Err + Expr _id_ [4-4]: Err + Expr _id_ [5-5]: Err + Expr _id_ [7-8]: Path: Path _id_ [7-8] (Ident _id_ [7-8] "y") + + [ + Error( + MissingSeqEntry( + Span { + lo: 3, + hi: 3, + }, + ), + ), + Error( + MissingSeqEntry( + Span { + lo: 4, + hi: 4, + }, + ), + ), + Error( + MissingSeqEntry( + Span { + lo: 5, + hi: 5, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn invalid_initial_comma_in_tuple() { + check( + expr, + "(, x)", + &expect![[r#" + Expr _id_ [0-5]: Tuple: + Expr _id_ [1-1]: Err + Expr _id_ [3-4]: Path: Path _id_ [3-4] (Ident _id_ [3-4] "x") + + [ + Error( + MissingSeqEntry( + Span { + lo: 1, + hi: 1, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn many_invalid_initial_commas_in_tuple() { + check( + expr, + "(,,,, x)", + &expect![[r#" + Expr _id_ [0-8]: Tuple: + Expr _id_ [1-1]: Err + Expr _id_ [2-2]: Err + Expr _id_ [3-3]: Err + Expr _id_ [4-4]: Err + Expr _id_ [6-7]: Path: Path _id_ [6-7] (Ident _id_ [6-7] "x") + + [ + Error( + MissingSeqEntry( + Span { + lo: 1, + hi: 1, + }, + ), + ), + Error( + MissingSeqEntry( + Span { + lo: 2, + hi: 2, + }, + ), + ), + Error( + MissingSeqEntry( + Span { + lo: 3, + hi: 3, + }, + ), + ), + Error( + MissingSeqEntry( + Span { + lo: 4, + hi: 4, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn duplicate_commas_in_pattern() { + check( + expr, + "set (x,, y) = (1, 2)", + &expect![[r#" + Expr _id_ [0-20]: Assign: + Expr _id_ [4-11]: Tuple: + Expr _id_ [5-6]: Path: Path _id_ [5-6] (Ident _id_ [5-6] "x") + Expr _id_ [7-7]: Err + Expr _id_ [9-10]: Path: Path _id_ [9-10] (Ident _id_ [9-10] "y") + Expr _id_ [14-20]: Tuple: + Expr _id_ [15-16]: Lit: Int(1) + Expr _id_ [18-19]: Lit: Int(2) + + [ + Error( + MissingSeqEntry( + Span { + lo: 7, + hi: 7, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn invalid_initial_commas_in_pattern() { + check( + expr, + "set (, x) = (1, 2)", + &expect![[r#" + Expr _id_ [0-18]: Assign: + Expr _id_ [4-9]: Tuple: + Expr _id_ [5-5]: Err + Expr _id_ [7-8]: Path: Path _id_ [7-8] (Ident _id_ [7-8] "x") + Expr _id_ [12-18]: Tuple: + Expr _id_ [13-14]: Lit: Int(1) + Expr _id_ [16-17]: Lit: Int(2) + + [ + Error( + MissingSeqEntry( + Span { + lo: 5, + hi: 5, + }, + ), + ), + ]"#]], + ); +} diff --git a/compiler/qsc_parse/src/item.rs b/compiler/qsc_parse/src/item.rs index 552d854ca6..412b788dde 100644 --- a/compiler/qsc_parse/src/item.rs +++ b/compiler/qsc_parse/src/item.rs @@ -230,6 +230,7 @@ fn try_tydef_as_ty(tydef: &TyDef) -> Option { kind: Box::new(TyKind::Tuple(ty_tup.into_boxed_slice())), }) } + TyDefKind::Err => None, } } diff --git a/compiler/qsc_parse/src/item/tests.rs b/compiler/qsc_parse/src/item/tests.rs index ceb877c25f..a83a4692f7 100644 --- a/compiler/qsc_parse/src/item/tests.rs +++ b/compiler/qsc_parse/src/item/tests.rs @@ -300,6 +300,86 @@ fn ty_def_tuple_lambda_args() { ); } +#[test] +fn ty_def_duplicate_comma() { + check( + parse, + "newtype Foo = (Int,, Int);", + &expect![[r#" + Item _id_ [0-26]: + New Type (Ident _id_ [8-11] "Foo"): TyDef _id_ [14-25]: Tuple: + TyDef _id_ [15-18]: Field: + Type _id_ [15-18]: Path: Path _id_ [15-18] (Ident _id_ [15-18] "Int") + TyDef _id_ [19-19]: Err + TyDef _id_ [21-24]: Field: + Type _id_ [21-24]: Path: Path _id_ [21-24] (Ident _id_ [21-24] "Int") + + [ + Error( + MissingSeqEntry( + Span { + lo: 19, + hi: 19, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn ty_def_initial_comma() { + check( + parse, + "newtype Foo = (, Int);", + &expect![[r#" + Item _id_ [0-22]: + New Type (Ident _id_ [8-11] "Foo"): TyDef _id_ [14-21]: Tuple: + TyDef _id_ [15-15]: Err + TyDef _id_ [17-20]: Field: + Type _id_ [17-20]: Path: Path _id_ [17-20] (Ident _id_ [17-20] "Int") + + [ + Error( + MissingSeqEntry( + Span { + lo: 15, + hi: 15, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn ty_def_named_duplicate_comma() { + check( + parse, + "newtype Foo = (X : Int,, Int);", + &expect![[r#" + Item _id_ [0-30]: + New Type (Ident _id_ [8-11] "Foo"): TyDef _id_ [14-29]: Tuple: + TyDef _id_ [15-22]: Field: + Ident _id_ [15-16] "X" + Type _id_ [19-22]: Path: Path _id_ [19-22] (Ident _id_ [19-22] "Int") + TyDef _id_ [23-23]: Err + TyDef _id_ [25-28]: Field: + Type _id_ [25-28]: Path: Path _id_ [25-28] (Ident _id_ [25-28] "Int") + + [ + Error( + MissingSeqEntry( + Span { + lo: 23, + hi: 23, + }, + ), + ), + ]"#]], + ); +} + #[test] fn function_decl() { check( @@ -450,6 +530,36 @@ fn function_two_ty_params() { ); } +#[test] +fn function_duplicate_comma_in_ty_param() { + check( + parse, + "function Foo<'T,,>() : Unit { body intrinsic; }", + &expect![[r#" + Item _id_ [0-47]: + Callable _id_ [0-47] (Function): + name: Ident _id_ [9-12] "Foo" + generics: + Ident _id_ [13-15] "'T" + Ident _id_ [16-16] "" + input: Pat _id_ [18-20]: Unit + output: Type _id_ [23-27]: Path: Path _id_ [23-27] (Ident _id_ [23-27] "Unit") + body: Specializations: + SpecDecl _id_ [30-45] (Body): Gen: Intrinsic + + [ + Error( + MissingSeqEntry( + Span { + lo: 16, + hi: 16, + }, + ), + ), + ]"#]], + ); +} + #[test] fn function_single_impl() { check( diff --git a/compiler/qsc_parse/src/lib.rs b/compiler/qsc_parse/src/lib.rs index 839ef2cce4..834c43b065 100644 --- a/compiler/qsc_parse/src/lib.rs +++ b/compiler/qsc_parse/src/lib.rs @@ -70,6 +70,9 @@ enum ErrorKind { #[error("expected callable inputs to be parenthesized")] #[diagnostic(code("Qsc.Parse.MissingParens"))] MissingParens(#[label] Span), + #[error("missing entry in sequence")] + #[diagnostic(code("Qsc.Parse.MissingSeqEntry"))] + MissingSeqEntry(#[label] Span), } impl ErrorKind { @@ -86,6 +89,7 @@ impl ErrorKind { Self::FloatingDocComment(span) => Self::FloatingDocComment(span + offset), Self::FloatingAttr(span) => Self::FloatingAttr(span + offset), Self::FloatingVisibility(span) => Self::FloatingVisibility(span + offset), + Self::MissingSeqEntry(span) => Self::MissingSeqEntry(span + offset), } } } diff --git a/compiler/qsc_parse/src/prim.rs b/compiler/qsc_parse/src/prim.rs index e46a7d631f..d837453012 100644 --- a/compiler/qsc_parse/src/prim.rs +++ b/compiler/qsc_parse/src/prim.rs @@ -9,7 +9,7 @@ use crate::{ lex::{Delim, TokenKind}, ErrorKind, }; -use qsc_ast::ast::{Ident, NodeId, Pat, PatKind, Path}; +use qsc_ast::ast::{DefaultWithSpan, Ident, NodeId, Pat, PatKind, Path}; use qsc_data_structures::span::Span; #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -170,12 +170,29 @@ pub(super) fn many(s: &mut Scanner, mut p: impl Parser) -> Result> Ok(xs) } -pub(super) fn seq(s: &mut Scanner, mut p: impl Parser) -> Result<(Vec, FinalSep)> { +pub(super) fn seq(s: &mut Scanner, mut p: impl Parser) -> Result<(Vec, FinalSep)> +where + T: DefaultWithSpan, +{ let mut xs = Vec::new(); let mut final_sep = FinalSep::Missing; + while s.peek().kind == TokenKind::Comma { + let mut span = s.peek().span; + span.hi = span.lo; + s.push_error(Error(ErrorKind::MissingSeqEntry(span))); + xs.push(T::default_with_span(span)); + s.advance(); + } while let Some(x) = opt(s, &mut p)? { xs.push(x); if token(s, TokenKind::Comma).is_ok() { + while s.peek().kind == TokenKind::Comma { + let mut span = s.peek().span; + span.hi = span.lo; + s.push_error(Error(ErrorKind::MissingSeqEntry(span))); + xs.push(T::default_with_span(span)); + s.advance(); + } final_sep = FinalSep::Present; } else { final_sep = FinalSep::Missing; diff --git a/compiler/qsc_parse/src/stmt/tests.rs b/compiler/qsc_parse/src/stmt/tests.rs index bee9f08b1b..a7d8a5127e 100644 --- a/compiler/qsc_parse/src/stmt/tests.rs +++ b/compiler/qsc_parse/src/stmt/tests.rs @@ -149,6 +149,93 @@ fn use_invalid_init() { ); } +#[test] +fn use_tuple_duplicate_commas() { + check( + parse, + "use (q1,, q2) = (Qubit(),, Qubit());", + &expect![[r#" + Stmt _id_ [0-36]: Qubit (Fresh) + Pat _id_ [4-13]: Tuple: + Pat _id_ [5-7]: Bind: + Ident _id_ [5-7] "q1" + Pat _id_ [8-8]: Err + Pat _id_ [10-12]: Bind: + Ident _id_ [10-12] "q2" + QubitInit _id_ [16-35] Tuple: + QubitInit _id_ [17-24] Single + QubitInit _id_ [25-25] Err + QubitInit _id_ [27-34] Single + + [ + Error( + MissingSeqEntry( + Span { + lo: 8, + hi: 8, + }, + ), + ), + Error( + MissingSeqEntry( + Span { + lo: 25, + hi: 25, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn use_tuple_initial_commas() { + check( + parse, + "use (,, q1, q2) = (, Qubit(), Qubit());", + &expect![[r#" + Stmt _id_ [0-39]: Qubit (Fresh) + Pat _id_ [4-15]: Tuple: + Pat _id_ [5-5]: Err + Pat _id_ [6-6]: Err + Pat _id_ [8-10]: Bind: + Ident _id_ [8-10] "q1" + Pat _id_ [12-14]: Bind: + Ident _id_ [12-14] "q2" + QubitInit _id_ [18-38] Tuple: + QubitInit _id_ [19-19] Err + QubitInit _id_ [21-28] Single + QubitInit _id_ [30-37] Single + + [ + Error( + MissingSeqEntry( + Span { + lo: 5, + hi: 5, + }, + ), + ), + Error( + MissingSeqEntry( + Span { + lo: 6, + hi: 6, + }, + ), + ), + Error( + MissingSeqEntry( + Span { + lo: 19, + hi: 19, + }, + ), + ), + ]"#]], + ); +} + #[test] fn borrow_stmt() { check( diff --git a/compiler/qsc_passes/src/borrowck.rs b/compiler/qsc_passes/src/borrowck.rs index 3fc1938740..84181fb4d5 100644 --- a/compiler/qsc_passes/src/borrowck.rs +++ b/compiler/qsc_passes/src/borrowck.rs @@ -44,7 +44,7 @@ impl Checker { PatKind::Bind(ident) => { self.mutable.insert(ident.id); } - PatKind::Discard => {} + PatKind::Discard | PatKind::Err => {} PatKind::Tuple(tup) => { for pat in tup { self.track_pat(pat); diff --git a/compiler/qsc_passes/src/replace_qubit_allocation.rs b/compiler/qsc_passes/src/replace_qubit_allocation.rs index 59ec07d3e6..3a40e5ad00 100644 --- a/compiler/qsc_passes/src/replace_qubit_allocation.rs +++ b/compiler/qsc_passes/src/replace_qubit_allocation.rs @@ -54,6 +54,7 @@ impl<'a> ReplaceQubitAllocation<'a> { QubitInitKind::Array(e) => (true, Some(take(e))), QubitInitKind::Single => (true, None), QubitInitKind::Tuple(_) => (false, None), + QubitInitKind::Err => panic!("QubitInitKind::Err"), } } @@ -178,6 +179,7 @@ impl<'a> ReplaceQubitAllocation<'a> { }; (tuple_expr, ids) } + QubitInitKind::Err => panic!("QubitInitKind::Err"), } } @@ -448,6 +450,7 @@ fn create_qubit_global_alloc( .collect(), ), }, + QubitInitKind::Err => panic!("QubitInitKind::Err"), } } diff --git a/language_service/src/display.rs b/language_service/src/display.rs index 57d3464b8c..4e7e592bde 100644 --- a/language_service/src/display.rs +++ b/language_service/src/display.rs @@ -306,6 +306,7 @@ impl<'a> Display for HirPat<'a> { write!(f, "()") } } + hir::PatKind::Err => write!(f, "?"), } } } @@ -376,6 +377,7 @@ impl<'a> Display for AstPat<'a> { write!(f, "()") } } + ast::PatKind::Err => write!(f, "?"), } } } @@ -639,6 +641,7 @@ impl<'a> Display for AstTy<'a> { write!(f, ")") } } + ast::TyKind::Err => write!(f, "?"), } } } @@ -698,6 +701,7 @@ impl<'a> Display for TyDef<'a> { write!(f, ")") } } + ast::TyDefKind::Err => write!(f, "?"), } } } diff --git a/language_service/src/hover.rs b/language_service/src/hover.rs index ebf57babf4..aac43c4255 100644 --- a/language_service/src/hover.rs +++ b/language_service/src/hover.rs @@ -233,7 +233,7 @@ fn is_param(param_pats: &[&ast::Pat], node_id: ast::NodeId) -> bool { fn find_in_pat(pat: &ast::Pat, node_id: ast::NodeId) -> bool { match &*pat.kind { ast::PatKind::Bind(ident, _) => node_id == ident.id, - ast::PatKind::Discard(_) | ast::PatKind::Elided => false, + ast::PatKind::Discard(_) | ast::PatKind::Elided | ast::PatKind::Err => false, ast::PatKind::Paren(inner) => find_in_pat(inner, node_id), ast::PatKind::Tuple(inner) => inner.iter().any(|x| find_in_pat(x, node_id)), } diff --git a/language_service/src/signature_help.rs b/language_service/src/signature_help.rs index 16b2d43afc..77413b3de7 100644 --- a/language_service/src/signature_help.rs +++ b/language_service/src/signature_help.rs @@ -145,7 +145,7 @@ impl SignatureHelpFinder<'_> { let mut offset = self.display.get_param_offset(package_id, decl); match &decl.input.kind { - hir::PatKind::Discard | hir::PatKind::Bind(_) => { + hir::PatKind::Discard | hir::PatKind::Err | hir::PatKind::Bind(_) => { self.make_wrapped_params(offset, package_id, &decl.input, doc) } hir::PatKind::Tuple(_) => { @@ -198,7 +198,7 @@ impl SignatureHelpFinder<'_> { doc: &str, ) -> Vec { match &pat.kind { - hir::PatKind::Bind(_) | hir::PatKind::Discard => { + hir::PatKind::Bind(_) | hir::PatKind::Discard | hir::PatKind::Err => { let documentation = if let hir::PatKind::Bind(name) = &pat.kind { let documentation = parse_doc_for_param(doc, &name.name); (!documentation.is_empty()).then_some(documentation) @@ -376,7 +376,7 @@ fn make_fake_pat(ty: &hir::ty::Ty) -> hir::Pat { fn process_args(args: &ast::Expr, location: u32, params: &hir::Pat) -> u32 { fn count_params(params: &hir::Pat) -> i32 { match ¶ms.kind { - hir::PatKind::Bind(_) | hir::PatKind::Discard => 1, + hir::PatKind::Bind(_) | hir::PatKind::Discard | hir::PatKind::Err => 1, hir::PatKind::Tuple(items) => items.iter().map(count_params).sum::() + 1, } } diff --git a/language_service/src/signature_help/tests.rs b/language_service/src/signature_help/tests.rs index 161e9b6236..4b991e9de7 100644 --- a/language_service/src/signature_help/tests.rs +++ b/language_service/src/signature_help/tests.rs @@ -244,7 +244,6 @@ fn last_argument() { ); } -#[ignore = "Parser needs updating to handle `(1,, \"Four\")`"] #[test] fn insert_second_argument() { check( @@ -257,7 +256,48 @@ fn insert_second_argument() { } } "#}, - &expect![[r#""#]], + &expect![[r#" + SignatureHelp { + signatures: [ + SignatureInformation { + label: "operation Foo(x : Int, y : Double, z : String) : Unit", + documentation: None, + parameters: [ + ParameterInformation { + label: Span { + start: 13, + end: 46, + }, + documentation: None, + }, + ParameterInformation { + label: Span { + start: 14, + end: 21, + }, + documentation: None, + }, + ParameterInformation { + label: Span { + start: 23, + end: 33, + }, + documentation: None, + }, + ParameterInformation { + label: Span { + start: 35, + end: 45, + }, + documentation: None, + }, + ], + }, + ], + active_signature: 0, + active_parameter: 2, + } + "#]], ); } diff --git a/library/std/arithmetic.qs b/library/std/arithmetic.qs index 6de8d02fd7..0dca6d61ed 100644 --- a/library/std/arithmetic.qs +++ b/library/std/arithmetic.qs @@ -324,14 +324,14 @@ namespace Microsoft.Quantum.Arithmetic { // // - // Operation: Add | Ripple-carry | Carry look-ahead - // ____________________________________________________________ - // y += 5 | RippleCarryIncByL✔| N/A - // y += x | RippleCarryIncByLE✔| LookAheadIncByLE - // z = x + 5 (z was 0) | N/A | N/A - // z = x + y (z was 0) | RippleCarryAddLE✔| LookAheadAddLE - // z += x + 5 | N/A | N/A - // z += x + y | N/A | N/A + // Operation: Add | General | Ripple-carry | Carry look-ahead | Fourier + // ____________________|_________|____________________|__________________|________________ + // y += 5 | IncByL | RippleCarryIncByL | | + // y += x | IncByLE | RippleCarryIncByLE | LookAheadIncByLE | FourierIncByLE + // z = x + 5 (z was 0) | | | | + // z = x + y (z was 0) | AddLE | RippleCarryAddLE | LookAheadAddLE | + // z += x + 5 | | | | + // z += x + y | | | | // /// # Summary @@ -342,7 +342,6 @@ namespace Microsoft.Quantum.Arithmetic { /// Length(ys) = n > 0, c is a BigInt number, 0 ≤ c < 2ⁿ. /// NOTE: Use RippleCarryIncByL directly if the choice of implementation /// is important. - @Config(Full) operation IncByL (c : BigInt, ys : Qubit[]) : Unit is Adj + Ctl { RippleCarryIncByL(c, ys); } @@ -355,7 +354,6 @@ namespace Microsoft.Quantum.Arithmetic { /// and Length(xs) ≤ Length(ys) = n. /// NOTE: Use RippleCarryIncByLE or LookAheadIncByLE directly if /// the choice of implementation is important. - @Config(Full) operation IncByLE (xs : Qubit[], ys : Qubit[]) : Unit is Adj + Ctl { RippleCarryIncByLE(xs, ys); } @@ -369,7 +367,6 @@ namespace Microsoft.Quantum.Arithmetic { /// Length(xs) = Length(ys) ≤ Length(zs) = n, assuming zs is 0-initialized. /// NOTE: Use RippleCarryAddLE or LookAheadAddLE directly if /// the choice of implementation is important. - @Config(Full) operation AddLE (xs : Qubit[], ys : Qubit[], zs : Qubit[]) : Unit is Adj { RippleCarryAddLE(xs, ys, zs); } @@ -386,7 +383,6 @@ namespace Microsoft.Quantum.Arithmetic { /// # Reference /// - [arXiv:1709.06648](https://arxiv.org/pdf/1709.06648.pdf) /// "Halving the cost of quantum addition" by Craig Gidney. - @Config(Full) operation RippleCarryIncByL (c : BigInt, ys : Qubit[]) : Unit is Adj + Ctl { let ysLen = Length(ys); Fact(ysLen > 0, "Length of `ys` must be at least 1."); @@ -422,7 +418,6 @@ namespace Microsoft.Quantum.Arithmetic { /// # Reference /// - [arXiv:1709.06648](https://arxiv.org/pdf/1709.06648.pdf) /// "Halving the cost of quantum addition" by Craig Gidney. - @Config(Full) operation RippleCarryIncByLE (xs : Qubit[], ys : Qubit[]) : Unit is Adj + Ctl { let xsLen = Length(xs); let ysLen = Length(ys); @@ -468,7 +463,6 @@ namespace Microsoft.Quantum.Arithmetic { /// # Reference /// - [arXiv:1709.06648](https://arxiv.org/pdf/1709.06648.pdf) /// "Halving the cost of quantum addition" by Craig Gidney. - @Config(Full) operation RippleCarryAddLE (xs : Qubit[], ys : Qubit[], zs : Qubit[]) : Unit is Adj { let xsLen = Length(xs); let zsLen = Length(zs); @@ -488,4 +482,74 @@ namespace Microsoft.Quantum.Arithmetic { } } + /// # Summary + /// Sets a zero-initialized little-endian register zs to the sum of + /// little-endian registers xs and ys using the carry-lookahead algorithm. + /// + /// # Description + /// Computes zs := xs + ys + zs[0] modulo 2ⁿ, where xs, ys, and zs are + /// little-endian registers, Length(xs) = Length(ys) ≤ Length(zs) = n, + /// assuming zs is 0-initialized, except for maybe zs[0], which can be + /// in |0> or |1> state and can be used as carry-in. + /// NOTE: `zs[Length(xs)]` can be used as carry-out, if `zs` is longer than `xs`. + /// This operation uses the carry-lookahead algorithm. + /// + /// # Reference + /// - [arXiv:quant-ph/0406142](https://arxiv.org/abs/quant-ph/0406142) + /// "A logarithmic-depth quantum carry-lookahead adder" by + /// Thomas G. Draper, Samuel A. Kutin, Eric M. Rains, Krysta M. Svore + operation LookAheadAddLE(xs : Qubit[], ys : Qubit[], zs : Qubit[]) : Unit is Adj { + let xsLen = Length(xs); + let zsLen = Length(zs); + Fact(Length(ys) == xsLen, "Registers `xs` and `ys` must be of same length."); + Fact(zsLen >= xsLen, "Register `zs` must be no shorter than register `xs`."); + + if zsLen > xsLen { // with carry-out + // compute initial generate values + for k in 0..xsLen - 1 { + ApplyAndAssuming0Target(xs[k], ys[k], zs[k + 1]); + } + + within { + // compute initial propagate values + ApplyToEachA(CNOT, Zipped(xs, ys)); + } apply { + if xsLen > 1 { + ComputeCarries(Rest(ys), zs[1..xsLen]); + } + + // compute sum into carries + for k in 0..xsLen - 1 { + CNOT(ys[k], zs[k]); + } + } + } else { // xsLen == zsLen, so without carry-out + LookAheadAddLE(Most(xs), Most(ys), zs); + CNOT(Tail(xs), Tail(zs)); + CNOT(Tail(ys), Tail(zs)); + } + } + + /// # Summary + /// Increments a little-endian register ys by a little-endian register xs + /// using Quantum Fourier Transform. + /// + /// # Description + /// Computes ys += xs modulo 2ⁿ, where xs and ys are little-endian registers, + /// and Length(xs) = Length(ys) = n. + /// This operation uses Quantum Fourier Transform. + /// + /// # Reference + /// - [arXiv:quant-ph/0008033](https://arxiv.org/abs/quant-ph/0008033) + /// "Addition on a Quantum Computer" by Thomas G. Draper + operation FourierIncByLE (xs : Qubit[], ys : Qubit[]) : Unit is Adj { + within { + ApplyQFT(ys); + } apply { + for (i, q) in Enumerated(xs) { + Controlled PhaseGradient([q], ys[i...]); + } + } + } + } diff --git a/library/std/arithmetic_internal.qs b/library/std/arithmetic_internal.qs index b1620a96f9..8418ae4671 100644 --- a/library/std/arithmetic_internal.qs +++ b/library/std/arithmetic_internal.qs @@ -5,9 +5,10 @@ namespace Microsoft.Quantum.Arithmetic { open Microsoft.Quantum.Diagnostics; open Microsoft.Quantum.Arrays; open Microsoft.Quantum.Math; + open Microsoft.Quantum.Convert; + // Computes ys += xs + carryIn using a ripple carry architecture - @Config(Full) internal operation IncWithCarryIn(carryIn : Qubit, xs : Qubit[], ys : Qubit[]) : Unit is Adj + Ctl { // We assume that it has already been checked that xs and ys are of @@ -35,7 +36,6 @@ namespace Microsoft.Quantum.Arithmetic { /// # Summary /// Implements Half-adder. Adds qubit x to qubit y and sets carryOut appropriately - @Config(Full) internal operation HalfAdderForInc(x : Qubit, y : Qubit, carryOut : Qubit) : Unit is Adj + Ctl { body (...) { @@ -62,7 +62,6 @@ namespace Microsoft.Quantum.Arithmetic { /// # Summary /// Implements Full-adder. Adds qubit carryIn and x to qubit y and sets carryOut appropriately. - @Config(Full) internal operation FullAdderForInc(carryIn : Qubit, x : Qubit, y : Qubit, carryOut : Qubit) : Unit is Adj + Ctl { body (...) { @@ -90,7 +89,6 @@ namespace Microsoft.Quantum.Arithmetic { } // Computes carryOut := carryIn + x + y - @Config(Full) internal operation FullAdder(carryIn : Qubit, x : Qubit, y : Qubit, carryOut : Qubit) : Unit is Adj { CNOT(x, y); @@ -103,7 +101,6 @@ namespace Microsoft.Quantum.Arithmetic { /// # Summary /// Computes carry bit for a full adder. - @Config(Full) internal operation CarryForInc(carryIn : Qubit, x : Qubit, y : Qubit, carryOut : Qubit) : Unit is Adj + Ctl { body (...) { @@ -125,7 +122,6 @@ namespace Microsoft.Quantum.Arithmetic { /// # Summary /// Uncomputes carry bit for a full adder. - @Config(Full) internal operation UncarryForInc(carryIn : Qubit, x : Qubit, y : Qubit, carryOut : Qubit) : Unit is Adj + Ctl { body (...) { @@ -151,18 +147,45 @@ namespace Microsoft.Quantum.Arithmetic { /// # Summary /// Applies AND gate between `control1` and `control2` and stores the result - /// in `target` assuming `target` is in |0> state. This allows for an - /// otimized adjoint implementation. + /// in `target` assuming `target` is in |0> state. + /// + /// # Description + /// Inverts `target` if and only if both controls are 1, but assumes that + /// `target` is in state 0. The operation has T-count 4, T-depth 2 and + /// requires no helper qubit, and may therefore be preferable to a CCNOT + /// operation, if `target` is known to be 0. + /// The adjoint of this operation is measurement based and requires no T + /// gates (but requires target to support branching on measurements). + /// Although the Toffoli gate (CCNOT) will perform faster in in simulations, + /// this version has lower T gate requirements. + /// # References + /// - Cody Jones: "Novel constructions for the fault-tolerant Toffoli gate", + /// Phys. Rev. A 87, 022328, 2013 + /// [arXiv:1212.5069](https://arxiv.org/abs/1212.5069) + /// doi:10.1103/PhysRevA.87.022328 @Config(Full) internal operation ApplyAndAssuming0Target(control1 : Qubit, control2 : Qubit, target: Qubit) - : Unit is Adj { + : Unit is Adj { // NOTE: Eventually this operation will be public and intrinsic. body (...) { if not CheckZero(target) { fail "ApplyAndAssuming0Target expects `target` to be in |0> state."; } - CCNOT(control1, control2, target); + H(target); + T(target); + CNOT(control1, target); + CNOT(control2, target); + within { + CNOT(target, control1); + CNOT(target, control2); + } + apply { + Adjoint T(control1); + Adjoint T(control2); + T(target); + } + H(target); + S(target); } - adjoint (...) { H(target); if M(target) == One { @@ -172,4 +195,136 @@ namespace Microsoft.Quantum.Arithmetic { } } + /// # Summary + /// Applies AND gate between `control1` and `control2` and stores the result + /// in `target` assuming `target` is in |0> state. + /// + /// # Description + /// Inverts `target` if and only if both controls are 1, but assumes that + /// `target` is in state 0. The operation has T-count 4, T-depth 2 and + /// requires no helper qubit, and may therefore be preferable to a CCNOT + /// operation, if `target` is known to be 0. + /// This version is suitable for Base profile. + /// Although the Toffoli gate (CCNOT) will perform faster in in simulations, + /// this version has lower T gate requirements. + /// # References + /// - Cody Jones: "Novel constructions for the fault-tolerant Toffoli gate", + /// Phys. Rev. A 87, 022328, 2013 + /// [arXiv:1212.5069](https://arxiv.org/abs/1212.5069) + /// doi:10.1103/PhysRevA.87.022328 + @Config(Base) + internal operation ApplyAndAssuming0Target(control1 : Qubit, control2 : Qubit, target: Qubit) + : Unit is Adj { + H(target); + T(target); + CNOT(control1, target); + CNOT(control2, target); + within { + CNOT(target, control1); + CNOT(target, control2); + } + apply { + Adjoint T(control1); + Adjoint T(control2); + T(target); + } + H(target); + S(target); + } + + /// # Summary + /// Computes carries for the look-ahead adder + internal operation ComputeCarries(ps : Qubit[], gs : Qubit[]) : Unit is Adj { + let n = Length(gs); + Fact(Length(ps)+1 == n, "Register gs must be one qubit longer than register gs."); + + let T = Floor(Lg(IntAsDouble(n))); + use qs = Qubit[n - HammingWeightI(n) - T]; + + let registerPartition = MappedOverRange(t -> Floor(IntAsDouble(n) / IntAsDouble(2^t)) - 1, 1..T - 1); + let pWorkspace = [ps] + Partitioned(registerPartition, qs); + + within { + PRounds(pWorkspace); + } apply { + // U_G + GRounds(pWorkspace, gs); + + // U_C + CRounds(pWorkspace, gs); + } + } + + /// # Summary + /// Computes all p[i, j] values in workspace for the look-ahead adder. + /// + /// The register array `pWorkspace` has T entries, where T = ⌊log₂ n⌋. + /// + /// The first entry `pWorkspace[0]` is initialized with `P_0` which is + /// computed before `ComputeCarries` is called. The other registers are + /// 0-initialized and will be computed in successive rounds t = 1, ..., T - 1. + /// + /// In each round t we compute + /// + /// p[i, j] = p[2ᵗ × m, 2ᵗ × (m + 1)] = p[i, k] ∧ p[k, j] + /// + /// in `pWorkspace[t][m - 1]` and use that for k = 2ᵗ × m + 2ᵗ⁻¹, p[i, k] and p[k, j] + /// have already been computed in round t - 1 in `pWorkspace[t - 1][2 * m - 1]` and + /// `pWorkspace[t - 1][2 * m]`, respectively. + internal operation PRounds(pWorkspace : Qubit[][]) : Unit is Adj { + for ws in Windows(2, pWorkspace) { + // note that we are using Rest, since pWorkspace[t - 1][0] is never + // accessed in round t. + let (current, next) = (Rest(ws[0]), ws[1]); + + for (m, target) in Enumerated(next) { + ApplyAndAssuming0Target(current[2 * m], current[2 * m + 1], target); + } + } + } + + /// # Summary + /// Computes g[i ∧ (i + 1), i + 1] into gs[i] for the look-ahead adder. + /// + /// The register gs has n entries initialized to gs[i] = g[i, i + 1]. + /// + /// After successive rounds t = 1, ..., T, the register is updated to + /// gs[i] = g[i ∧ (i + 1), i + 1], from which we can compute the carries + /// in the C-rounds. + internal operation GRounds(pWorkspace : Qubit[][], gs : Qubit[]) : Unit is Adj { + let T = Length(pWorkspace); + let n = Length(gs); + + for t in 1..T { + let length = Floor(IntAsDouble(n) / IntAsDouble(2^t)) - 1; + let ps = pWorkspace[t - 1][0..2...]; + + for m in 0..length { + CCNOT(gs[2^t * m + 2^(t - 1) - 1], ps[m], gs[2^t * m + 2^t - 1]); + } + } + } + + /// # Summary + /// Computes carries into gs for the look-ahead adder. + internal operation CRounds(pWorkspace : Qubit[][], gs : Qubit[]) : Unit is Adj { + let n = Length(gs); + + let start = Floor(Lg(IntAsDouble(2 * n) / 3.0)); + for t in start..-1..1 { + let length = Floor(IntAsDouble(n - 2^(t - 1)) / IntAsDouble(2^t)); + let ps = pWorkspace[t - 1][1..2...]; + + for m in 1..length { + CCNOT(gs[2^t * m - 1], ps[m - 1], gs[2^t * m + 2^(t - 1) - 1]); + } + } + } + + internal operation PhaseGradient (qs : Qubit[]) : Unit is Adj + Ctl { + for (i, q) in Enumerated(qs) { + R1Frac(1, i, q); + } + } + } diff --git a/library/std/canon.qs b/library/std/canon.qs index 694fd32a8b..8c9098b118 100644 --- a/library/std/canon.qs +++ b/library/std/canon.qs @@ -476,4 +476,43 @@ namespace Microsoft.Quantum.Canon { } } + /// # Summary + /// Applies Quantum Fourier Transform (QFT) to a little-endian quantum register. + /// + /// # Description + /// Applies QFT to a little-endian register `qs` of length n + /// containing |x₁⟩⊗|x₂⟩⊗…⊗|xₙ⟩. The qs[0] contains the + /// least significant bit xₙ. The state of qs[0] becomes + /// (|0⟩+𝑒^(2π𝑖[0.xₙ])|1⟩)/sqrt(2) after the operation. + /// + /// # Input + /// ## qs + /// Quantum register in a little-endian format to which the QFT is applied. + /// + /// # Reference + /// - [Quantum Fourier transform](https://en.wikipedia.org/wiki/Quantum_Fourier_transform) + operation ApplyQFT (qs : Qubit[]) : Unit is Adj + Ctl { + let length = Length(qs); + Fact(length >= 1, "ApplyQFT: Length(qs) must be at least 1."); + for i in length-1..-1..0 { + H(qs[i]); + for j in 0..i-1 { + Controlled R1Frac([qs[i]], (1, j+1, qs[i-j-1])); + } + } + } + + /// # Summary + /// Uses SWAP gates to reverse the order of the qubits in a register. + /// + /// # Input + /// ## register + /// The qubits order of which should be reversed using SWAP gates + operation SwapReverseRegister (register : Qubit[]) : Unit is Adj + Ctl { + let length = Length(register); + for i in 0 .. length/2 - 1 { + SWAP(register[i], register[(length - i) - 1]); + } + } + } diff --git a/library/std/math.qs b/library/std/math.qs index 6a62e590cf..e0b1eb92d8 100644 --- a/library/std/math.qs +++ b/library/std/math.qs @@ -716,6 +716,14 @@ namespace Microsoft.Quantum.Math { count } + /// # Summary + /// Returns the number of 1 bits in the binary representation of integer `n`. + function HammingWeightI (n : Int) : Int { + let i1 = n - ((n >>> 1) &&& 0x5555555555555555); + let i2 = (i1 &&& 0x3333333333333333) + ((i1 >>> 2) &&& 0x3333333333333333); + // Multiplication may overflow. See https://github.com/microsoft/qsharp/issues/828 + (((i2 + (i2 >>> 4)) &&& 0xF0F0F0F0F0F0F0F) * 0x101010101010101) >>> 56 + } // // Combinatorics diff --git a/library/tests/src/lib.rs b/library/tests/src/lib.rs index ba698168d2..13652a2860 100644 --- a/library/tests/src/lib.rs +++ b/library/tests/src/lib.rs @@ -9,6 +9,8 @@ mod test_arithmetic; #[cfg(test)] mod test_arrays; #[cfg(test)] +mod test_canon; +#[cfg(test)] mod test_convert; #[cfg(test)] mod test_math; diff --git a/library/tests/src/resources/fourier_inc_by_le.qs b/library/tests/src/resources/fourier_inc_by_le.qs new file mode 100644 index 0000000000..19fe89088a --- /dev/null +++ b/library/tests/src/resources/fourier_inc_by_le.qs @@ -0,0 +1,43 @@ +namespace Test { + open Microsoft.Quantum.Arithmetic; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Diagnostics; + + internal operation TestFourierIncByLE(n : Int) : Unit { + use xs = Qubit[n]; + use ys = Qubit[n]; + + for xsValue in 0..(1 <<< n) - 1 { + for ysValue in 0..(1 <<< n) - 1 { + ApplyXorInPlace(xsValue, xs); + ApplyXorInPlace(ysValue, ys); + FourierIncByLE(xs, ys); + Fact(MeasureInteger(ys) == (xsValue + ysValue) % (1 <<< n), + $"TestFourierIncByLE (|xs|=|ys|): Incorrect ys for xs={xsValue}, ys={ysValue}"); + Fact(MeasureInteger(xs) == xsValue, + $"TestFourierIncByLE (|xs|=|ys|): Incorrect xs for xs={xsValue}, ys={ysValue}"); + ResetAll(xs); + ResetAll(ys); + } + } + + let ysL = n+2; + use xs = Qubit[n]; + use ys = Qubit[ysL]; + + for xsValue in 0..(1 <<< n) - 1 { + for ysValue in 0..(1 <<< ysL) - 1 { + ApplyXorInPlace(xsValue, xs); + ApplyXorInPlace(ysValue, ys); + FourierIncByLE(xs, ys); + Fact(MeasureInteger(ys) == (xsValue + ysValue) % (1 <<< ysL), + $"TestFourierIncByLE (|xs|<|ys|): Incorrect ys for xs={xsValue}, ys={ysValue}"); + Fact(MeasureInteger(xs) == xsValue, + $"TestFourierIncByLE (|xs|<|ys|): Incorrect xs for xs={xsValue}, ys={ysValue}"); + ResetAll(xs); + ResetAll(ys); + } + } + } + +} diff --git a/library/tests/src/resources/lookahead_add_le.qs b/library/tests/src/resources/lookahead_add_le.qs new file mode 100644 index 0000000000..390104ee1d --- /dev/null +++ b/library/tests/src/resources/lookahead_add_le.qs @@ -0,0 +1,51 @@ +namespace Test { + open Microsoft.Quantum.Arithmetic; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Diagnostics; + + operation TestLookAheadAddLE(n : Int) : Unit { + use xs = Qubit[n]; + use ys = Qubit[n]; + use zs = Qubit[n]; + + for xsValue in 0..(1 <<< n) - 1 { + for ysValue in 0..(1 <<< n) - 1 { + ApplyXorInPlace(xsValue, xs); + ApplyXorInPlace(ysValue, ys); + LookAheadAddLE(xs, ys, zs); + Fact(MeasureInteger(xs) == xsValue, + $"TestLookAheadAddLE (|xs|=|zs|): Incorrect xs for xs={xsValue}, ys={ysValue}"); + Fact(MeasureInteger(ys) == ysValue, + $"TestLookAheadAddLE (|xs|=|zs|): Incorrect ys for xs={xsValue}, ys={ysValue}"); + Fact(MeasureInteger(zs) == (xsValue + ysValue) % (1 <<< n), + $"TestLookAheadAddLE (|xs|=|zs|): Incorrect zs for xs={xsValue}, ys={ysValue}"); + ResetAll(xs); + ResetAll(ys); + ResetAll(zs); + } + } + + let zsL = n+1; + use xs = Qubit[n]; + use ys = Qubit[n]; + use zs = Qubit[zsL]; + + for xsValue in 0..(1 <<< n) - 1 { + for ysValue in 0..(1 <<< n) - 1 { + ApplyXorInPlace(xsValue, xs); + ApplyXorInPlace(ysValue, ys); + LookAheadAddLE(xs, ys, zs); + Fact(MeasureInteger(xs) == xsValue, + $"TestLookAheadAddLE (|xs|<|zs|): Incorrect xs for xs={xsValue}, ys={ysValue}"); + Fact(MeasureInteger(ys) == ysValue, + $"TestLookAheadAddLE (|xs|<|zs|): Incorrect ys for xs={xsValue}, ys={ysValue}"); + Fact(MeasureInteger(zs) == (xsValue + ysValue) % (1 <<< zsL), + $"TestLookAheadAddLE (|xs|<|zs|): Incorrect zs for xs={xsValue}, ys={ysValue}"); + ResetAll(xs); + ResetAll(ys); + ResetAll(zs); + } + } + } + +} diff --git a/library/tests/src/resources/qft_le.qs b/library/tests/src/resources/qft_le.qs new file mode 100644 index 0000000000..9c1341e687 --- /dev/null +++ b/library/tests/src/resources/qft_le.qs @@ -0,0 +1,88 @@ +namespace Test { + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Arrays; + + operation PrepareEntangledState ( + left : Qubit[], + right : Qubit[]) : Unit is Adj + Ctl { + + for idxQubit in 0 .. Length(left) - 1 + { + H(left[idxQubit]); + Controlled X([left[idxQubit]], right[idxQubit]); + } + } + + operation AssertOperationsEqualReferenced ( + nQubits : Int, + actual : (Qubit[] => Unit), + expected : (Qubit[] => Unit is Adj)) : Unit { + + // Prepare a reference register entangled with the target register. + use (reference, target) = (Qubit[nQubits], Qubit[nQubits]) { + PrepareEntangledState(reference, target); + actual(target); + Adjoint expected(target); + Adjoint PrepareEntangledState(reference, target); + let isCorrect = CheckAllZero(reference + target); + ResetAll(target); + ResetAll(reference); + Fact(isCorrect, "OPERATIONS ARE NOT THE SAME!"); + } + } + + /// # Summary + /// Hard-code 1 qubit QFT + operation QFT1 (target : Qubit[]) : Unit is Adj { + Fact(Length(target) == 1, $"`Length(target!)` must be 1"); + H((target)[0]); + } + + /// # Summary + /// Hard-code 2 qubit QFT + operation QFT2 (target : Qubit[]) : Unit is Adj { + Fact(Length(target) == 2, $"`Length(target!)` must be 2"); + let (q1, q2) = ((target)[0], (target)[1]); + H(q1); + Controlled R1Frac([q2], (2, 2, q1)); + H(q2); + } + + /// # Summary + /// Hard-code 3 qubit QFT + operation QFT3 (target : Qubit[]) : Unit is Adj { + Fact(Length(target) == 3, $"`Length(target)` must be 3"); + let (q1, q2, q3) = ((target)[0], (target)[1], (target)[2]); + H(q1); + Controlled R1Frac([q2], (2, 2, q1)); + Controlled R1Frac([q3], (2, 3, q1)); + H(q2); + Controlled R1Frac([q3], (2, 2, q2)); + H(q3); + } + + /// # Summary + /// Hard-code 4 qubit QFT + operation QFT4 (target : Qubit[]) : Unit is Adj { + Fact(Length(target) == 4, $"`Length(target!)` must be 4"); + let (q1, q2, q3, q4) = ((target)[0], (target)[1], (target)[2], (target)[3]); + H(q1); + Controlled R1Frac([q2], (2, 2, q1)); + Controlled R1Frac([q3], (2, 3, q1)); + Controlled R1Frac([q4], (2, 4, q1)); + H(q2); + Controlled R1Frac([q3], (2, 2, q2)); + Controlled R1Frac([q4], (2, 3, q2)); + H(q3); + Controlled R1Frac([q4], (2, 2, q3)); + H(q4); + } + + /// # Summary + /// Compares QFT to the hard-coded implementations + operation TestQFT(n: Int) : Unit { + Fact(n>=1 and n<=4, "Only have four tests for QFT."); + let testOperations = [QFT1, QFT2, QFT3, QFT4]; + AssertOperationsEqualReferenced(n, testOperations[n-1], q => ApplyQFT(Reversed(q))); + } +} diff --git a/library/tests/src/resources/ripple_carry_add_le.qs b/library/tests/src/resources/ripple_carry_add_le.qs index dc76e79123..93f0f7eed6 100644 --- a/library/tests/src/resources/ripple_carry_add_le.qs +++ b/library/tests/src/resources/ripple_carry_add_le.qs @@ -4,7 +4,6 @@ namespace Test { open Microsoft.Quantum.Diagnostics; operation TestRippleCarryAddLE(n : Int) : Unit { - use xs = Qubit[n]; use ys = Qubit[n]; use zs = Qubit[n]; @@ -14,27 +13,34 @@ namespace Test { ApplyXorInPlace(xsValue, xs); ApplyXorInPlace(ysValue, ys); RippleCarryAddLE(xs, ys, zs); - Fact(MeasureInteger(xs) == xsValue, $"unexpected value for `xs` given xsValue = {xsValue} and ysValue = {ysValue}"); - Fact(MeasureInteger(ys) == ysValue, $"unexpected value for `ys` given xsValue = {xsValue} and ysValue = {ysValue}"); - Fact(MeasureInteger(zs) == (xsValue + ysValue) % (1 <<< n), $"unexpected value for `zs` given xsValue = {xsValue} and ysValue = {ysValue}"); + Fact(MeasureInteger(xs) == xsValue, + $"TestRippleCarryAddLE (|xs|=|zs|): Incorrect xs for xs={xsValue}, ys={ysValue}"); + Fact(MeasureInteger(ys) == ysValue, + $"TestRippleCarryAddLE (|xs|=|zs|): Incorrect ys for xs={xsValue}, ys={ysValue}"); + Fact(MeasureInteger(zs) == (xsValue + ysValue) % (1 <<< n), + $"TestRippleCarryAddLE (|xs|=|zs|): Incorrect zs for xs={xsValue}, ys={ysValue}"); ResetAll(xs); ResetAll(ys); ResetAll(zs); } } + let zsL = n+1; use xs = Qubit[n]; use ys = Qubit[n]; - use zs = Qubit[n + 1]; + use zs = Qubit[zsL]; for xsValue in 0..(1 <<< n) - 1 { for ysValue in 0..(1 <<< n) - 1 { ApplyXorInPlace(xsValue, xs); ApplyXorInPlace(ysValue, ys); RippleCarryAddLE(xs, ys, zs); - Fact(MeasureInteger(xs) == xsValue, $"unexpected value for `xs` given xsValue = {xsValue} and ysValue = {ysValue}"); - Fact(MeasureInteger(ys) == ysValue, $"unexpected value for `ys` given xsValue = {xsValue} and ysValue = {ysValue}"); - Fact(MeasureInteger(zs) == (xsValue + ysValue) % (1 <<< (n + 1)), $"unexpected value for `zs` given xsValue = {xsValue} and ysValue = {ysValue}"); + Fact(MeasureInteger(xs) == xsValue, + $"TestRippleCarryAddLE (|xs|<|zs|): Incorrect xs for xs={xsValue}, ys={ysValue}"); + Fact(MeasureInteger(ys) == ysValue, + $"TestRippleCarryAddLE (|xs|<|zs|): Incorrect ys for xs={xsValue}, ys={ysValue}"); + Fact(MeasureInteger(zs) == (xsValue + ysValue) % (1 <<< zsL), + $"TestRippleCarryAddLE (|xs|<|zs|): Incorrect zs for xs={xsValue}, ys={ysValue}"); ResetAll(xs); ResetAll(ys); ResetAll(zs); diff --git a/library/tests/src/resources/ripple_carry_inc_by_l.qs b/library/tests/src/resources/ripple_carry_inc_by_l.qs index 68e284a37c..6cc71821ae 100644 --- a/library/tests/src/resources/ripple_carry_inc_by_l.qs +++ b/library/tests/src/resources/ripple_carry_inc_by_l.qs @@ -10,7 +10,8 @@ namespace Test { for ysValue in 0..(1 <<< n) - 1 { ApplyXorInPlace(ysValue, ys); RippleCarryIncByL(IntAsBigInt(c), ys); - Fact(MeasureInteger(ys) == (c + ysValue) % (1 <<< n), $"unexpected value for `ys` given c = {c} and ysValue = {ysValue}"); + Fact(MeasureInteger(ys) == (c + ysValue) % (1 <<< n), + $"TestRippleCarryIncByL: Incorrect `ys` for c={c}, ys={ysValue}"); ResetAll(ys); } } @@ -30,7 +31,8 @@ namespace Test { } apply { ApplyXorInPlace(ysValue, ys); Controlled RippleCarryIncByL([ctl], (IntAsBigInt(c), ys)); - Fact(MeasureInteger(ys) == (isCtl ? (c + ysValue) % (1 <<< n) | ysValue), $"unexpected value for `ys` given c = {c} and ysValue = {ysValue}"); + Fact(MeasureInteger(ys) == (isCtl ? (c + ysValue) % (1 <<< n) | ysValue), + $"TestRippleCarryIncByLCtl: Incorrect `ys` for c={c}, ys={ysValue}"); } ResetAll(ys); Reset(ctl); diff --git a/library/tests/src/resources/ripple_carry_inc_by_le.qs b/library/tests/src/resources/ripple_carry_inc_by_le.qs index da74b7ef7f..f057c785a3 100644 --- a/library/tests/src/resources/ripple_carry_inc_by_le.qs +++ b/library/tests/src/resources/ripple_carry_inc_by_le.qs @@ -12,23 +12,28 @@ namespace Test { ApplyXorInPlace(xsValue, xs); ApplyXorInPlace(ysValue, ys); RippleCarryIncByLE(xs, ys); - Fact(MeasureInteger(ys) == (xsValue + ysValue) % (1 <<< n), $"unexpected value for `ys` given xsValue = {xsValue} and ysValue = {ysValue}"); - Fact(MeasureInteger(xs) == xsValue, $"unexpected value for `xs` given xsValue = {xsValue} and ysValue = {ysValue}"); + Fact(MeasureInteger(ys) == (xsValue + ysValue) % (1 <<< n), + $"TestRippleCarryIncByLE (|xs|=|ys|): Incorrect ys for xs={xsValue}, ys={ysValue}"); + Fact(MeasureInteger(xs) == xsValue, + $"TestRippleCarryIncByLE (|xs|=|ys|): Incorrect xs for xs={xsValue}, ys={ysValue}"); ResetAll(xs); ResetAll(ys); } } + let ysL = n+1; use xs = Qubit[n]; - use ys = Qubit[n + 1]; + use ys = Qubit[ysL]; for xsValue in 0..(1 <<< n) - 1 { - for ysValue in 0..(1 <<< (n + 1)) - 1 { + for ysValue in 0..(1 <<< ysL) - 1 { ApplyXorInPlace(xsValue, xs); ApplyXorInPlace(ysValue, ys); RippleCarryIncByLE(xs, ys); - Fact(MeasureInteger(ys) == (xsValue + ysValue) % (1 <<< (n + 1)), $"unexpected value for `ys` given xsValue = {xsValue} and ysValue = {ysValue}"); - Fact(MeasureInteger(xs) == xsValue, $"unexpected value for `xs` given xsValue = {xsValue} and ysValue = {ysValue}"); + Fact(MeasureInteger(ys) == (xsValue + ysValue) % (1 <<< ysL), + $"TestRippleCarryIncByLE (|xs|<|ys|): Incorrect ys for xs={xsValue}, ys={ysValue}"); + Fact(MeasureInteger(xs) == xsValue, + $"TestRippleCarryIncByLE (|xs|<|ys|): Incorrect xs for xs={xsValue}, ys={ysValue}"); ResetAll(xs); ResetAll(ys); } @@ -51,8 +56,10 @@ namespace Test { ApplyXorInPlace(xsValue, xs); ApplyXorInPlace(ysValue, ys); Controlled RippleCarryIncByLE([ctl], (xs, ys)); - Fact(MeasureInteger(ys) == (isCtl ? (xsValue + ysValue) % (1 <<< n) | ysValue), $"unexpected value for `ys` given xsValue = {xsValue} and ysValue = {ysValue}"); - Fact(MeasureInteger(xs) == xsValue, $"unexpected value for `xs` given xsValue = {xsValue} and ysValue = {ysValue}"); + Fact(MeasureInteger(ys) == (isCtl ? (xsValue + ysValue) % (1 <<< n) | ysValue), + $"TestRippleCarryIncByLECtl: Incorrect ys for xs={xsValue}, ys={ysValue}"); + Fact(MeasureInteger(xs) == xsValue, + $"TestRippleCarryIncByLECtl: Incorrect xs for xs={xsValue}, ys={ysValue}"); } ResetAll(xs); ResetAll(ys); diff --git a/library/tests/src/test_arithmetic.rs b/library/tests/src/test_arithmetic.rs index b1490f21e5..7a27ad0161 100644 --- a/library/tests/src/test_arithmetic.rs +++ b/library/tests/src/test_arithmetic.rs @@ -344,13 +344,13 @@ fn check_ripple_carry_inc_by_l_branching() { use y0 = Qubit[10]; ApplyXorInPlace(172, y0); - IncByL(128L,y0); + RippleCarryIncByL(128L,y0); let i0 = MeasureInteger(y0); ResetAll(y0); use y1 = Qubit[10]; ApplyXorInPlace(172, y1); - IncByL(0L,y1); + RippleCarryIncByL(0L,y1); let i1 = MeasureInteger(y1); ResetAll(y1); @@ -445,7 +445,7 @@ fn check_ripple_carry_inc_by_le_branching() { use x0 = Qubit[1]; use y0 = Qubit[2]; ApplyXorInPlace(3, y0); - IncByLE(x0,y0); + RippleCarryIncByLE(x0,y0); let i0 = MeasureInteger(y0); ResetAll(x0+y0); @@ -453,7 +453,7 @@ fn check_ripple_carry_inc_by_le_branching() { use y1 = Qubit[10]; ApplyXorInPlace(7, x1); ApplyXorInPlace(31, y1); - IncByLE(x1,y1); + RippleCarryIncByLE(x1,y1); let i1 = MeasureInteger(y1); ResetAll(x1+y1); @@ -461,7 +461,7 @@ fn check_ripple_carry_inc_by_le_branching() { use y2 = Qubit[4]; ApplyXorInPlace(7, x2); ApplyXorInPlace(7, y2); - IncByLE(x2,y2); + RippleCarryIncByLE(x2,y2); let i2 = MeasureInteger(y2); ResetAll(x2+y2); @@ -558,7 +558,7 @@ fn check_ripple_carry_add_le_branching() { use z0 = Qubit[3]; ApplyXorInPlace(3, x0); ApplyXorInPlace(3, y0); - AddLE(x0,y0,z0); + RippleCarryAddLE(x0,y0,z0); let i0 = MeasureInteger(z0); ResetAll(x0+y0+z0); @@ -567,7 +567,7 @@ fn check_ripple_carry_add_le_branching() { use z1 = Qubit[4]; ApplyXorInPlace(3, x1); ApplyXorInPlace(3, y1); - AddLE(x1,y1,z1); + RippleCarryAddLE(x1,y1,z1); let i1 = MeasureInteger(z1); ResetAll(x1+y1+z1); @@ -577,7 +577,7 @@ fn check_ripple_carry_add_le_branching() { ApplyXorInPlace(3, x2); ApplyXorInPlace(3, y2); X(z2[0]); - AddLE(x2,y2,z2); + RippleCarryAddLE(x2,y2,z2); let i2 = MeasureInteger(z2); ResetAll(x2+y2+z2); @@ -625,3 +625,112 @@ fn check_ripple_carry_add_le_exhaustive_bitwidth_4() { &Value::Tuple(vec![].into()), ); } + +#[test] +fn check_lookahead_add_le_branching() { + test_expression( + { + "{ // Branching cases for LookAheadAddLE + open Microsoft.Quantum.Arithmetic; + + use x0 = Qubit[2]; + use y0 = Qubit[2]; + use z0 = Qubit[3]; + ApplyXorInPlace(3, x0); + ApplyXorInPlace(3, y0); + LookAheadAddLE(x0,y0,z0); + let i0 = MeasureInteger(z0); + ResetAll(x0+y0+z0); + + use x1 = Qubit[2]; + use y1 = Qubit[2]; + use z1 = Qubit[4]; + ApplyXorInPlace(3, x1); + ApplyXorInPlace(3, y1); + LookAheadAddLE(x1,y1,z1); + let i1 = MeasureInteger(z1); + ResetAll(x1+y1+z1); + + use x2 = Qubit[2]; + use y2 = Qubit[2]; + use z2 = Qubit[4]; + ApplyXorInPlace(3, x2); + ApplyXorInPlace(3, y2); + X(z2[0]); + LookAheadAddLE(x2,y2,z2); + let i2 = MeasureInteger(z2); + ResetAll(x2+y2+z2); + + return (i0, i1, i2); + }" + }, + &Value::Tuple(vec![Value::Int(6), Value::Int(6), Value::Int(7)].into()), + ); +} + +const LOOKAHEAD_ADD_LE_TEST_LIB: &str = include_str!("resources/lookahead_add_le.qs"); + +#[test] +fn check_lookahead_add_le_exhaustive_bitwidth_1() { + test_expression_with_lib( + "Test.TestLookAheadAddLE(1)", + LOOKAHEAD_ADD_LE_TEST_LIB, + &Value::Tuple(vec![].into()), + ); +} + +#[test] +fn check_lookahead_add_le_exhaustive_bitwidth_2() { + test_expression_with_lib( + "Test.TestLookAheadAddLE(2)", + LOOKAHEAD_ADD_LE_TEST_LIB, + &Value::Tuple(vec![].into()), + ); +} + +#[test] +fn check_lookahead_add_le_exhaustive_bitwidth_3() { + test_expression_with_lib( + "Test.TestLookAheadAddLE(3)", + LOOKAHEAD_ADD_LE_TEST_LIB, + &Value::Tuple(vec![].into()), + ); +} + +#[test] +fn check_lookahead_add_le_exhaustive_bitwidth_4() { + test_expression_with_lib( + "Test.TestLookAheadAddLE(4)", + LOOKAHEAD_ADD_LE_TEST_LIB, + &Value::Tuple(vec![].into()), + ); +} + +const FOURIER_INC_BY_LE_TEST_LIB: &str = include_str!("resources/fourier_inc_by_le.qs"); + +#[test] +fn check_fourier_inc_by_le_exhaustive_bitwidth_1() { + test_expression_with_lib( + "Test.TestFourierIncByLE(1)", + FOURIER_INC_BY_LE_TEST_LIB, + &Value::Tuple(vec![].into()), + ); +} + +#[test] +fn check_fourier_inc_by_le_exhaustive_bitwidth_2() { + test_expression_with_lib( + "Test.TestFourierIncByLE(2)", + FOURIER_INC_BY_LE_TEST_LIB, + &Value::Tuple(vec![].into()), + ); +} + +#[test] +fn check_fourier_inc_by_le_exhaustive_bitwidth_3() { + test_expression_with_lib( + "Test.TestFourierIncByLE(3)", + FOURIER_INC_BY_LE_TEST_LIB, + &Value::Tuple(vec![].into()), + ); +} diff --git a/library/tests/src/test_canon.rs b/library/tests/src/test_canon.rs new file mode 100644 index 0000000000..9bdc6a149e --- /dev/null +++ b/library/tests/src/test_canon.rs @@ -0,0 +1,335 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::{test_expression, test_expression_with_lib}; +use indoc::indoc; +use qsc::interpret::Value; + +// Tests for Microsoft.Quantum.Canon namespace + +#[test] +fn check_apply_to_each() { + test_expression( + indoc! {r#"{ + use register = Qubit[3]; + Microsoft.Quantum.Canon.ApplyToEach(X, register); + let results = Microsoft.Quantum.Measurement.MeasureEachZ(register); + ResetAll(register); + results + }"#}, + &Value::Array(vec![Value::RESULT_ONE, Value::RESULT_ONE, Value::RESULT_ONE].into()), + ); +} + +#[test] +fn check_apply_to_each_a() { + test_expression( + indoc! {r#"{ + use register = Qubit[3]; + Microsoft.Quantum.Canon.ApplyToEach(X, register); + Adjoint Microsoft.Quantum.Canon.ApplyToEachA(X, register); + let results = Microsoft.Quantum.Measurement.MResetEachZ(register); + results + }"#}, + &Value::Array(vec![Value::RESULT_ZERO, Value::RESULT_ZERO, Value::RESULT_ZERO].into()), + ); +} + +#[test] +fn check_apply_to_each_c_applied() { + test_expression( + indoc! {r#"{ + use control = Qubit(); + use register = Qubit[3]; + Controlled Microsoft.Quantum.Canon.ApplyToEachC([control], (X, register)); + let results = Microsoft.Quantum.Measurement.MResetEachZ(register); + Reset(control); + results + }"#}, + &Value::Array(vec![Value::RESULT_ZERO, Value::RESULT_ZERO, Value::RESULT_ZERO].into()), + ); +} + +#[test] +fn check_apply_to_each_c_not_applied() { + test_expression( + indoc! {r#"{ + use control = Qubit(); + use register = Qubit[3]; + X(control); + Controlled Microsoft.Quantum.Canon.ApplyToEachC([control], (X, register)); + let results = Microsoft.Quantum.Measurement.MResetEachZ(register); + Reset(control); + results + }"#}, + &Value::Array(vec![Value::RESULT_ONE, Value::RESULT_ONE, Value::RESULT_ONE].into()), + ); +} + +#[test] +fn check_apply_to_each_ca_applied() { + test_expression( + indoc! {r#"{ + use control = Qubit(); + use register = Qubit[3]; + Microsoft.Quantum.Canon.ApplyToEach(X, register); + Controlled Adjoint Microsoft.Quantum.Canon.ApplyToEachCA([control], (X, register)); + let results = Microsoft.Quantum.Measurement.MResetEachZ(register); + Reset(control); + results + }"#}, + &Value::Array(vec![Value::RESULT_ONE, Value::RESULT_ONE, Value::RESULT_ONE].into()), + ); +} + +#[test] +fn check_apply_to_each_ca_not_applied() { + test_expression( + indoc! {r#"{ + use control = Qubit(); + use register = Qubit[3]; + X(control); + Microsoft.Quantum.Canon.ApplyToEach(X, register); + Controlled Adjoint Microsoft.Quantum.Canon.ApplyToEachCA([control], (X, register)); + let results = Microsoft.Quantum.Measurement.MResetEachZ(register); + Reset(control); + results + }"#}, + &Value::Array(vec![Value::RESULT_ZERO, Value::RESULT_ZERO, Value::RESULT_ZERO].into()), + ); +} + +#[test] +fn check_fst_snd() { + test_expression("Fst(7,6)", &Value::Int(7)); + test_expression("Snd(7,6)", &Value::Int(6)); +} + +#[test] +fn check_apply_cnot_chain_2() { + test_expression( + { + "{ + use a = Qubit[2]; + mutable result = []; + within { + X(a[0]); + X(a[1]); + ApplyCNOTChain(a); + } + apply { + set result = [M(a[0]),M(a[1])]; + } + return result; + }" + }, + &Value::Array(vec![Value::RESULT_ONE, Value::RESULT_ZERO].into()), + ); +} + +#[test] +fn check_apply_cnot_chain_3() { + test_expression( + { + "{ + use a = Qubit[3]; + mutable result = []; + within { + X(a[0]); + ApplyCNOTChain(a); + } + apply { + set result = [M(a[0]),M(a[1]),M(a[2])]; + } + return result; + }" + }, + &Value::Array(vec![Value::RESULT_ONE, Value::RESULT_ONE, Value::RESULT_ONE].into()), + ); +} + +#[test] +fn check_apply_cnot_chain_3a() { + test_expression( + { + "{ + use a = Qubit[3]; + mutable result = []; + within { + X(a[0]); + X(a[2]); + ApplyCNOTChain(a); + } + apply { + set result = [M(a[0]),M(a[1]),M(a[2])]; + } + return result; + }" + }, + &Value::Array(vec![Value::RESULT_ONE, Value::RESULT_ONE, Value::RESULT_ZERO].into()), + ); +} + +#[test] +fn check_apply_p() { + test_expression( + { + "{ + open Microsoft.Quantum.Measurement; + use q = Qubit[3]; + ApplyP(PauliX, q[0]); + H(q[1]); ApplyP(PauliY, q[1]); + H(q[2]); S(q[2]); ApplyP(PauliZ, q[2]); + return [MResetZ(q[0]),MResetX(q[1]),MResetY(q[2])]; + }" + }, + &Value::Array(vec![Value::RESULT_ONE, Value::RESULT_ONE, Value::RESULT_ONE].into()), + ); +} + +#[test] +fn check_apply_pauli() { + test_expression( + { + "{ + open Microsoft.Quantum.Measurement; + use q = Qubit[3]; + H(q[1]); + H(q[2]); S(q[2]); + ApplyPauli([PauliX, PauliY, PauliZ], q); + return [MResetZ(q[0]),MResetX(q[1]),MResetY(q[2])]; + }" + }, + &Value::Array(vec![Value::RESULT_ONE, Value::RESULT_ONE, Value::RESULT_ONE].into()), + ); +} + +#[test] +fn check_apply_pauli_from_bit_string() { + test_expression( + { + "{ + open Microsoft.Quantum.Measurement; + use q = Qubit[3]; + ApplyPauliFromBitString(PauliX, false, [true, false, true], q); + return MResetEachZ(q); + }" + }, + &Value::Array(vec![Value::RESULT_ZERO, Value::RESULT_ONE, Value::RESULT_ZERO].into()), + ); +} + +#[test] +fn check_apply_pauli_from_int() { + test_expression( + { + "{ + open Microsoft.Quantum.Measurement; + use q = Qubit[3]; + ApplyPauliFromInt(PauliX, false, 5, q); + return MResetEachZ(q); + }" + }, + &Value::Array(vec![Value::RESULT_ZERO, Value::RESULT_ONE, Value::RESULT_ZERO].into()), + ); +} + +#[test] +fn check_apply_controlled_on_int() { + test_expression( + { + "{ + open Microsoft.Quantum.Measurement; + use c = Qubit[3]; + use t1 = Qubit(); + use t2 = Qubit(); + within { + X(c[0]); + X(c[2]); + } apply { + ApplyControlledOnInt(5, X, c, t1); + } + ApplyControlledOnInt(5, X, c, t2); + return [MResetZ(t1), M(t2)]; + }" + }, + &Value::Array(vec![Value::RESULT_ONE, Value::RESULT_ZERO].into()), + ); +} + +#[test] +fn check_apply_controlled_on_bitstring() { + test_expression( + { + "{ + open Microsoft.Quantum.Measurement; + use c = Qubit[4]; + use t1 = Qubit(); + use t2 = Qubit(); + within { + X(c[0]); + X(c[2]); + } apply { + ApplyControlledOnBitString([true, false, true], X, c, t1); + } + ApplyControlledOnBitString([true, false, true], X, c, t2); + return [MResetZ(t1), M(t2)]; + }" + }, + &Value::Array(vec![Value::RESULT_ONE, Value::RESULT_ZERO].into()), + ); +} + +const QFT_LE_TEST_LIB: &str = include_str!("resources/qft_le.qs"); + +#[test] +fn check_qft_le_sample_1() { + test_expression_with_lib( + "Test.TestQFT(1)", + QFT_LE_TEST_LIB, + &Value::Tuple(vec![].into()), + ); +} + +#[test] +fn check_qft_le_sample_2() { + test_expression_with_lib( + "Test.TestQFT(2)", + QFT_LE_TEST_LIB, + &Value::Tuple(vec![].into()), + ); +} +#[test] +fn check_qft_le_sample_3() { + test_expression_with_lib( + "Test.TestQFT(3)", + QFT_LE_TEST_LIB, + &Value::Tuple(vec![].into()), + ); +} +#[test] +fn check_qft_le_sample_4() { + test_expression_with_lib( + "Test.TestQFT(4)", + QFT_LE_TEST_LIB, + &Value::Tuple(vec![].into()), + ); +} + +#[test] +fn check_swap_reverse_register() { + test_expression( + { + "{ + open Microsoft.Quantum.Arithmetic; + use q = Qubit[10]; + ApplyXorInPlace(328, q); + SwapReverseRegister(q); + let r = MeasureInteger(q); + ResetAll(q); + r + }" + }, + &Value::Int(74), + ); +} diff --git a/library/tests/src/test_math.rs b/library/tests/src/test_math.rs index bbf1342ca0..3264dd0466 100644 --- a/library/tests/src/test_math.rs +++ b/library/tests/src/test_math.rs @@ -629,6 +629,48 @@ fn check_bitsize_l() { ); } +#[test] +fn check_trailing_zero_count_i() { + test_expression( + "Microsoft.Quantum.Math.TrailingZeroCountI(7)", + &Value::Int(0), + ); + test_expression( + "Microsoft.Quantum.Math.TrailingZeroCountI(2)", + &Value::Int(1), + ); + test_expression( + "Microsoft.Quantum.Math.TrailingZeroCountI(7616)", + &Value::Int(6), + ); +} + +#[test] +fn check_trailing_zero_count_l() { + test_expression( + "Microsoft.Quantum.Math.TrailingZeroCountL(7L)", + &Value::Int(0), + ); + test_expression( + "Microsoft.Quantum.Math.TrailingZeroCountL(2L)", + &Value::Int(1), + ); + test_expression( + "Microsoft.Quantum.Math.TrailingZeroCountL(1L<<<163)", + &Value::Int(163), + ); +} + +#[test] +fn check_hamming_weight() { + test_expression("Microsoft.Quantum.Math.HammingWeightI(2)", &Value::Int(1)); + test_expression("Microsoft.Quantum.Math.HammingWeightI(14)", &Value::Int(3)); + test_expression( + "Microsoft.Quantum.Math.HammingWeightI(1<<<5)", + &Value::Int(1), + ); +} + // // Combinatorics // diff --git a/library/tests/src/tests.rs b/library/tests/src/tests.rs index dd64d53148..54b8dc4b17 100644 --- a/library/tests/src/tests.rs +++ b/library/tests/src/tests.rs @@ -5,112 +5,9 @@ use crate::test_expression; use indoc::indoc; use qsc::interpret::Value; -// -// Canon namespace -// - -#[test] -fn check_apply_to_each() { - test_expression( - indoc! {r#"{ - use register = Qubit[3]; - Microsoft.Quantum.Canon.ApplyToEach(X, register); - let results = Microsoft.Quantum.Measurement.MeasureEachZ(register); - ResetAll(register); - results - }"#}, - &Value::Array(vec![Value::RESULT_ONE, Value::RESULT_ONE, Value::RESULT_ONE].into()), - ); -} - -#[test] -fn check_apply_to_each_a() { - test_expression( - indoc! {r#"{ - use register = Qubit[3]; - Microsoft.Quantum.Canon.ApplyToEach(X, register); - Adjoint Microsoft.Quantum.Canon.ApplyToEachA(X, register); - let results = Microsoft.Quantum.Measurement.MResetEachZ(register); - results - }"#}, - &Value::Array(vec![Value::RESULT_ZERO, Value::RESULT_ZERO, Value::RESULT_ZERO].into()), - ); -} - -#[test] -fn check_apply_to_each_c_applied() { - test_expression( - indoc! {r#"{ - use control = Qubit(); - use register = Qubit[3]; - Controlled Microsoft.Quantum.Canon.ApplyToEachC([control], (X, register)); - let results = Microsoft.Quantum.Measurement.MResetEachZ(register); - Reset(control); - results - }"#}, - &Value::Array(vec![Value::RESULT_ZERO, Value::RESULT_ZERO, Value::RESULT_ZERO].into()), - ); -} - -#[test] -fn check_apply_to_each_c_not_applied() { - test_expression( - indoc! {r#"{ - use control = Qubit(); - use register = Qubit[3]; - X(control); - Controlled Microsoft.Quantum.Canon.ApplyToEachC([control], (X, register)); - let results = Microsoft.Quantum.Measurement.MResetEachZ(register); - Reset(control); - results - }"#}, - &Value::Array(vec![Value::RESULT_ONE, Value::RESULT_ONE, Value::RESULT_ONE].into()), - ); -} - -#[test] -fn check_apply_to_each_ca_applied() { - test_expression( - indoc! {r#"{ - use control = Qubit(); - use register = Qubit[3]; - Microsoft.Quantum.Canon.ApplyToEach(X, register); - Controlled Adjoint Microsoft.Quantum.Canon.ApplyToEachCA([control], (X, register)); - let results = Microsoft.Quantum.Measurement.MResetEachZ(register); - Reset(control); - results - }"#}, - &Value::Array(vec![Value::RESULT_ONE, Value::RESULT_ONE, Value::RESULT_ONE].into()), - ); -} - -#[test] -fn check_apply_to_each_ca_not_applied() { - test_expression( - indoc! {r#"{ - use control = Qubit(); - use register = Qubit[3]; - X(control); - Microsoft.Quantum.Canon.ApplyToEach(X, register); - Controlled Adjoint Microsoft.Quantum.Canon.ApplyToEachCA([control], (X, register)); - let results = Microsoft.Quantum.Measurement.MResetEachZ(register); - Reset(control); - results - }"#}, - &Value::Array(vec![Value::RESULT_ZERO, Value::RESULT_ZERO, Value::RESULT_ZERO].into()), - ); -} - // // Core namespace // - -#[test] -fn check_fst_snd() { - test_expression("Fst(7,6)", &Value::Int(7)); - test_expression("Snd(7,6)", &Value::Int(6)); -} - #[test] fn check_repeated() { test_expression("Repeated(Zero, 0)", &Value::Array(vec![].into())); @@ -128,181 +25,6 @@ fn check_repeated() { ); } -#[test] -fn check_apply_cnot_chain_2() { - test_expression( - { - "{ - use a = Qubit[2]; - mutable result = []; - within { - X(a[0]); - X(a[1]); - ApplyCNOTChain(a); - } - apply { - set result = [M(a[0]),M(a[1])]; - } - return result; - }" - }, - &Value::Array(vec![Value::RESULT_ONE, Value::RESULT_ZERO].into()), - ); -} - -#[test] -fn check_apply_cnot_chain_3() { - test_expression( - { - "{ - use a = Qubit[3]; - mutable result = []; - within { - X(a[0]); - ApplyCNOTChain(a); - } - apply { - set result = [M(a[0]),M(a[1]),M(a[2])]; - } - return result; - }" - }, - &Value::Array(vec![Value::RESULT_ONE, Value::RESULT_ONE, Value::RESULT_ONE].into()), - ); -} - -#[test] -fn check_apply_p() { - test_expression( - { - "{ - open Microsoft.Quantum.Measurement; - use q = Qubit[3]; - ApplyP(PauliX, q[0]); - H(q[1]); ApplyP(PauliY, q[1]); - H(q[2]); S(q[2]); ApplyP(PauliZ, q[2]); - return [MResetZ(q[0]),MResetX(q[1]),MResetY(q[2])]; - }" - }, - &Value::Array(vec![Value::RESULT_ONE, Value::RESULT_ONE, Value::RESULT_ONE].into()), - ); -} - -#[test] -fn check_apply_pauli() { - test_expression( - { - "{ - open Microsoft.Quantum.Measurement; - use q = Qubit[3]; - H(q[1]); - H(q[2]); S(q[2]); - ApplyPauli([PauliX, PauliY, PauliZ], q); - return [MResetZ(q[0]),MResetX(q[1]),MResetY(q[2])]; - }" - }, - &Value::Array(vec![Value::RESULT_ONE, Value::RESULT_ONE, Value::RESULT_ONE].into()), - ); -} - -#[test] -fn check_apply_pauli_from_bit_string() { - test_expression( - { - "{ - open Microsoft.Quantum.Measurement; - use q = Qubit[3]; - ApplyPauliFromBitString(PauliX, false, [true, false, true], q); - return MResetEachZ(q); - }" - }, - &Value::Array(vec![Value::RESULT_ZERO, Value::RESULT_ONE, Value::RESULT_ZERO].into()), - ); -} - -#[test] -fn check_apply_pauli_from_int() { - test_expression( - { - "{ - open Microsoft.Quantum.Measurement; - use q = Qubit[3]; - ApplyPauliFromInt(PauliX, false, 5, q); - return MResetEachZ(q); - }" - }, - &Value::Array(vec![Value::RESULT_ZERO, Value::RESULT_ONE, Value::RESULT_ZERO].into()), - ); -} - -#[test] -fn check_apply_controlled_on_int() { - test_expression( - { - "{ - open Microsoft.Quantum.Measurement; - use c = Qubit[3]; - use t1 = Qubit(); - use t2 = Qubit(); - within { - X(c[0]); - X(c[2]); - } apply { - ApplyControlledOnInt(5, X, c, t1); - } - ApplyControlledOnInt(5, X, c, t2); - return [MResetZ(t1), M(t2)]; - }" - }, - &Value::Array(vec![Value::RESULT_ONE, Value::RESULT_ZERO].into()), - ); -} - -#[test] -fn check_apply_controlled_on_bitstring() { - test_expression( - { - "{ - open Microsoft.Quantum.Measurement; - use c = Qubit[4]; - use t1 = Qubit(); - use t2 = Qubit(); - within { - X(c[0]); - X(c[2]); - } apply { - ApplyControlledOnBitString([true, false, true], X, c, t1); - } - ApplyControlledOnBitString([true, false, true], X, c, t2); - return [MResetZ(t1), M(t2)]; - }" - }, - &Value::Array(vec![Value::RESULT_ONE, Value::RESULT_ZERO].into()), - ); -} - -#[test] -fn check_apply_cnot_chain_3a() { - test_expression( - { - "{ - use a = Qubit[3]; - mutable result = []; - within { - X(a[0]); - X(a[2]); - ApplyCNOTChain(a); - } - apply { - set result = [M(a[0]),M(a[1]),M(a[2])]; - } - return result; - }" - }, - &Value::Array(vec![Value::RESULT_ONE, Value::RESULT_ONE, Value::RESULT_ZERO].into()), - ); -} - #[test] fn check_exp_with_cnot() { // This decomposition only holds if the magnitude of the angle used in Exp is correct and if the