Skip to content

Commit 29c6fd5

Browse files
Fix bugs in Adjoint generation (#293)
This change fixes several bugs in adjoint generation and conjugate handling, namely: - Correctly generate pattern including `ctls` when generating `controlled adjoint invert` - Prefer `controlled adjoint invert` when an explicit `controlled` implementation is present - Fix propagation of "quantum-ness" from nested inner expressions with blocks, such as conjugate-expr inside of if-expr - Disallows return expressions from inside of apply-blocks (Fixes #289) - Preserves types and output values for transformed conjugate expressions as supported by the type system --------- Co-authored-by: DmitryVasilevsky <[email protected]>
1 parent 4b25175 commit 29c6fd5

File tree

7 files changed

+349
-118
lines changed

7 files changed

+349
-118
lines changed

compiler/qsc_eval/src/tests.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2562,6 +2562,11 @@ fn global_callable_as_arg() {
25622562
);
25632563
}
25642564

2565+
#[test]
2566+
fn conjugate_output_preserved() {
2567+
check_expr("", "{let x = within{}apply{4}; x}", &expect!["4"]);
2568+
}
2569+
25652570
#[test]
25662571
fn interpolated_string() {
25672572
check_expr("", r#"$"string""#, &expect!["string"]);

compiler/qsc_passes/src/conjugate_invert.rs

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ use qsc_data_structures::span::Span;
1111
use qsc_frontend::compile::CompileUnit;
1212
use qsc_hir::{
1313
assigner::Assigner,
14-
hir::{Block, CallableDecl, Expr, ExprKind, NodeId, Res, Stmt, StmtKind, Ty},
14+
hir::{
15+
Block, CallableDecl, Expr, ExprKind, Ident, Mutability, NodeId, Pat, PatKind, Res, Stmt,
16+
StmtKind, Ty,
17+
},
1518
mut_visit::{self, MutVisitor},
1619
visit::{self, Visitor},
1720
};
@@ -31,6 +34,9 @@ pub enum Error {
3134
#[error("variable cannot be assigned in apply-block since it is used in within-block")]
3235
#[diagnostic(help("updating mutable variables in the apply-block that are used in the within-block can violate logic reversibility"))]
3336
ApplyAssign(#[label] Span),
37+
38+
#[error("return expressions are not allowed in apply-blocks")]
39+
ReturnForbidden(#[label] Span),
3440
}
3541

3642
/// Generates adjoint inverted blocks for within-blocks across all conjugate expressions,
@@ -72,8 +78,6 @@ struct ConjugateElim<'a> {
7278

7379
impl<'a> MutVisitor for ConjugateElim<'a> {
7480
fn visit_expr(&mut self, expr: &mut Expr) {
75-
mut_visit::walk_expr(self, expr);
76-
7781
match take(&mut expr.kind) {
7882
ExprKind::Conjugate(within, apply) => {
7983
let mut usage = Usage {
@@ -87,6 +91,10 @@ impl<'a> MutVisitor for ConjugateElim<'a> {
8791
assign_check.visit_block(&apply);
8892
self.errors.extend(assign_check.errors);
8993

94+
let mut return_check = ReturnCheck { errors: Vec::new() };
95+
return_check.visit_block(&apply);
96+
self.errors.extend(return_check.errors);
97+
9098
let mut adj_within = within.clone();
9199
if let Err(invert_errors) = adj_invert_block(self.assigner, &mut adj_within) {
92100
self.errors.extend(
@@ -102,28 +110,43 @@ impl<'a> MutVisitor for ConjugateElim<'a> {
102110
self.errors
103111
.extend(distrib.errors.into_iter().map(Error::AdjGen));
104112

113+
let (bind_id, apply_as_bind) =
114+
block_as_binding(apply, expr.ty.clone(), self.assigner);
115+
105116
let new_block = Block {
106117
id: NodeId::default(),
107118
span: Span::default(),
108-
ty: Ty::UNIT,
119+
ty: expr.ty.clone(),
109120
stmts: vec![
110121
block_as_stmt(within),
111-
block_as_stmt(apply),
122+
apply_as_bind,
112123
block_as_stmt(adj_within),
124+
Stmt {
125+
id: NodeId::default(),
126+
span: Span::default(),
127+
kind: StmtKind::Expr(Expr {
128+
id: NodeId::default(),
129+
span: Span::default(),
130+
ty: expr.ty.clone(),
131+
kind: ExprKind::Var(Res::Local(bind_id)),
132+
}),
133+
},
113134
],
114135
};
115-
*expr = block_as_expr(new_block);
136+
*expr = block_as_expr(new_block, expr.ty.clone());
116137
}
117138
kind => expr.kind = kind,
118139
}
140+
141+
mut_visit::walk_expr(self, expr);
119142
}
120143
}
121144

122-
fn block_as_expr(block: Block) -> Expr {
145+
fn block_as_expr(block: Block, ty: Ty) -> Expr {
123146
Expr {
124147
id: NodeId::default(),
125148
span: Span::default(),
126-
ty: Ty::UNIT,
149+
ty,
127150
kind: ExprKind::Block(block),
128151
}
129152
}
@@ -132,10 +155,35 @@ fn block_as_stmt(block: Block) -> Stmt {
132155
Stmt {
133156
id: NodeId::default(),
134157
span: Span::default(),
135-
kind: StmtKind::Expr(block_as_expr(block)),
158+
kind: StmtKind::Expr(block_as_expr(block, Ty::UNIT)),
136159
}
137160
}
138161

162+
fn block_as_binding(block: Block, ty: Ty, assigner: &mut Assigner) -> (NodeId, Stmt) {
163+
let bind_id = assigner.next_id();
164+
(
165+
bind_id,
166+
Stmt {
167+
id: assigner.next_id(),
168+
span: Span::default(),
169+
kind: StmtKind::Local(
170+
Mutability::Immutable,
171+
Pat {
172+
id: assigner.next_id(),
173+
span: Span::default(),
174+
ty: ty.clone(),
175+
kind: PatKind::Bind(Ident {
176+
id: bind_id,
177+
span: Span::default(),
178+
name: "apply_res".into(),
179+
}),
180+
},
181+
block_as_expr(block, ty),
182+
),
183+
},
184+
)
185+
}
186+
139187
struct Usage {
140188
used: HashSet<NodeId>,
141189
}
@@ -186,3 +234,15 @@ impl AssignmentCheck {
186234
}
187235
}
188236
}
237+
238+
struct ReturnCheck {
239+
errors: Vec<Error>,
240+
}
241+
242+
impl<'a> Visitor<'a> for ReturnCheck {
243+
fn visit_expr(&mut self, expr: &'a Expr) {
244+
if matches!(&expr.kind, ExprKind::Return(..)) {
245+
self.errors.push(Error::ReturnForbidden(expr.span));
246+
}
247+
}
248+
}

0 commit comments

Comments
 (0)