Skip to content

Commit

Permalink
Relabel in adaptive conditional block should be disallowed (#2155)
Browse files Browse the repository at this point in the history
This adds a check at codegen time if a qubit relabel is occurring in an
adaptive conditional. This will ensure we don't generate QIR with
incorrect qubit IDs. The change ties the error to the dynamic qubit
capability because if/when we support dynamic qubits we'll need to
update relabel to become an intrinsic that is supported by backends to
do a runtime relabel of qubit identifiers.

Fixes #2152
  • Loading branch information
swernli authored Feb 4, 2025
1 parent acd1890 commit e24e690
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 3 deletions.
7 changes: 7 additions & 0 deletions compiler/qsc_partial_eval/src/evaluation_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ impl EvaluationContext {
pub fn push_scope(&mut self, s: Scope) {
self.scopes.push(s);
}

/// Determines whether we are currently in a dynamic branch context for any scope.
pub fn is_currently_evaluating_any_branch(&self) -> bool {
self.scopes
.iter()
.any(Scope::is_currently_evaluating_branch)
}
}

/// Struct that represents a block node when we intepret an RIR program as a graph.
Expand Down
15 changes: 12 additions & 3 deletions compiler/qsc_partial_eval/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1583,9 +1583,18 @@ impl<'a> PartialEvaluator<'a> {
// Qubit allocations and measurements have special handling.
"__quantum__rt__qubit_allocate" => Ok(self.allocate_qubit()),
"__quantum__rt__qubit_release" => Ok(self.release_qubit(args_value)),
"PermuteLabels" => qubit_relabel(args_value, args_span, |q0, q1| {
self.resource_manager.swap_qubit_ids(q0, q1);
})
"PermuteLabels" => {
if self.eval_context.is_currently_evaluating_any_branch() {
// If we are in a dynamic branch anywhere up the call stack, we cannot support relabel,
// as later qubit usage would need to be dynamic on whether the branch was taken.
return Err(Error::CapabilityError(CapabilityError::UseOfDynamicQubit(
callee_expr_span.span,
)));
}
qubit_relabel(args_value, args_span, |q0, q1| {
self.resource_manager.swap_qubit_ids(q0, q1);
})
}
.map_err(std::convert::Into::into),
"__quantum__qis__m__body" => Ok(self.measure_qubit(builder::m_decl(), args_value)),
"__quantum__qis__mresetz__body" => {
Expand Down
20 changes: 20 additions & 0 deletions compiler/qsc_partial_eval/src/tests/qubits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,3 +360,23 @@ fn qubit_double_release_triggers_runtime_error() {
]],
);
}

#[test]
fn qubit_relabel_in_dynamic_block_triggers_capability_error() {
let error = get_partial_evaluation_error(indoc! {
r#"
operation Main() : Result {
use qs = Qubit[2];
if M(qs[0]) == One {
Relabel(qs, Std.Arrays.Reversed(qs));
}
MResetZ(qs[1])
}
"#,
});

assert_error(
&error,
&expect!["CapabilityError(UseOfDynamicQubit(Span { lo: 59760, hi: 59773 }))"],
);
}

0 comments on commit e24e690

Please sign in to comment.