Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions source/compiler/qsc_eval/src/val.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ pub enum VarTy {
Boolean,
Integer,
Double,
Qubit,
}

impl Display for VarTy {
Expand All @@ -244,6 +245,7 @@ impl Display for VarTy {
Self::Boolean => write!(f, "Boolean"),
Self::Integer => write!(f, "Integer"),
Self::Double => write!(f, "Double"),
Self::Qubit => write!(f, "Qubit"),
}
}
}
Expand Down
47 changes: 43 additions & 4 deletions source/compiler/qsc_partial_eval/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1155,6 +1155,13 @@ impl<'a> PartialEvaluator<'a> {
bin_op_expr_span,
)
}
VarTy::Qubit => Err(Error::Unexpected(
format!(
"unsupported LHS variable type {} in binary operation",
lhs_eval_var.ty
),
bin_op_expr_span,
)),
}
}

Expand Down Expand Up @@ -1540,6 +1547,23 @@ impl<'a> PartialEvaluator<'a> {
self.check_unresolved_call_capabilities(call_expr_id, callee_expr_id, &call_scope)?;
self.assign_current_dbg_location(call_expr_id);

if store_item_id.package == PackageId::CORE
&& callable_decl.name.name.as_ref() == "ReleaseQubitArray"
{
// This is a special case, where we must statically releaes the given qubits rather than call into the stdlib, which may try
// to unroll the loop over the qubits to be released. Instead, iterate over the qubits here and release them directly.
let Value::Array(qubit_vals) = args_value else {
return Err(Error::Unexpected(
"expected an array of qubits as argument to ReleaseQubitArray".to_string(),
args_span,
));
};
for qubit_val in qubit_vals.iter().cloned() {
self.release_qubit(qubit_val, args_span)?;
}
return Ok(EvalControlFlow::Continue(Value::unit()));
}

// We generate instructions differently depending on whether we are calling an intrinsic or a specialization
// with an implementation.
let value = match spec_decl {
Expand Down Expand Up @@ -1748,7 +1772,7 @@ impl<'a> PartialEvaluator<'a> {
"__quantum__rt__qubit_allocate" | "__quantum__rt__qubit_borrow" => {
Ok(self.allocate_qubit())
}
"__quantum__rt__qubit_release" => Ok(self.release_qubit(args_value)),
"__quantum__rt__qubit_release" => self.release_qubit(args_value, args_span),
"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,
Expand Down Expand Up @@ -1859,6 +1883,13 @@ impl<'a> PartialEvaluator<'a> {
let ret_val = match output_var {
None => Value::unit(),
Some(output_var) => {
if output_var.ty == rir::Ty::Prim(rir::Prim::Qubit) {
// We don't actually accept custom intrinsics that return qubits, so emit an error here.
return Err(Error::UnsupportedCustomIntrinsicType(
callable_decl.output.to_string(),
callee_expr_span,
));
}
let rir_var = map_rir_var_to_eval_var(output_var).map_err(|()| {
Error::UnsupportedCustomIntrinsicType(
callable_decl.output.to_string(),
Expand Down Expand Up @@ -3096,12 +3127,17 @@ impl<'a> PartialEvaluator<'a> {
result_value
}

fn release_qubit(&mut self, args_value: Value) -> Value {
let qubit = args_value.unwrap_qubit();
fn release_qubit(&mut self, args_value: Value, arg_span: PackageSpan) -> Result<Value, Error> {
let Value::Qubit(qubit) = args_value else {
return Err(Error::Unimplemented(
"release release of dynamic qubit variable".to_string(),
arg_span,
));
};
self.resource_manager.release_qubit(&qubit);

// The value of a qubit release is unit.
Value::unit()
Ok(Value::unit())
}

fn resolve_args(
Expand Down Expand Up @@ -4216,6 +4252,7 @@ fn map_eval_var_type_to_rir_type(var_ty: VarTy) -> rir::Ty {
VarTy::Boolean => rir::Ty::Prim(rir::Prim::Boolean),
VarTy::Integer => rir::Ty::Prim(rir::Prim::Integer),
VarTy::Double => rir::Ty::Prim(rir::Prim::Double),
VarTy::Qubit => rir::Ty::Prim(rir::Prim::Qubit),
}
}

Expand Down Expand Up @@ -4251,6 +4288,7 @@ fn map_rir_type_to_eval_var_type(ty: rir::Ty) -> Result<VarTy, ()> {
rir::Ty::Prim(rir::Prim::Boolean) => Ok(VarTy::Boolean),
rir::Ty::Prim(rir::Prim::Integer) => Ok(VarTy::Integer),
rir::Ty::Prim(rir::Prim::Double) => Ok(VarTy::Double),
rir::Ty::Prim(rir::Prim::Qubit) => Ok(VarTy::Qubit),
_ => Err(()),
}
}
Expand All @@ -4260,6 +4298,7 @@ fn try_get_eval_var_type(value: &Value) -> Option<VarTy> {
Value::Bool(_) => Some(VarTy::Boolean),
Value::Int(_) => Some(VarTy::Integer),
Value::Double(_) => Some(VarTy::Double),
Value::Qubit(_) => Some(VarTy::Qubit),
Value::Var(var) => Some(var.ty),
_ => None,
}
Expand Down
3 changes: 0 additions & 3 deletions source/compiler/qsc_partial_eval/src/tests/arrays.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1096,9 +1096,6 @@ fn result_array_index_range_returns_length_as_end() {
Variable(1, Integer) = Store Integer(1)
Call id(2), args( Qubit(1), Result(1), )
Variable(1, Integer) = Store Integer(2)
Variable(2, Integer) = Store Integer(0)
Variable(2, Integer) = Store Integer(1)
Variable(2, Integer) = Store Integer(2)
Call id(3), args( Integer(1), Tag(0, 3), )
Return
config: Config:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,10 +268,6 @@ fn nested_classical_for_loop() {
Call id(2), args( Qubit(2), ) !dbg dbg_location=35
Variable(4, Integer) = Store Integer(3)
Variable(1, Integer) = Store Integer(3)
Variable(5, Integer) = Store Integer(0)
Variable(5, Integer) = Store Integer(1)
Variable(5, Integer) = Store Integer(2)
Variable(5, Integer) = Store Integer(3)
Call id(3), args( Integer(0), Tag(0, 3), )
Return

Expand Down
32 changes: 14 additions & 18 deletions source/compiler/qsc_partial_eval/src/tests/loops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,6 @@ fn for_loop_over_arrays_of_tuples_unrolled() {
"#]].assert_eq(&program.to_string());
}

// For now, loops over qubits are unrolled since we don't support qubit variables.
#[test]
fn for_loop_over_qubits() {
let program = get_rir_program_with_capabilities(
Expand Down Expand Up @@ -761,24 +760,28 @@ fn for_loop_over_qubits() {
Variable(0, Integer) = Store Integer(2)
Variable(0, Integer) = Store Integer(3)
Variable(1, Integer) = Store Integer(0)
Call id(2), args( Qubit(0), )
Variable(1, Integer) = Store Integer(1)
Call id(2), args( Qubit(1), )
Variable(1, Integer) = Store Integer(2)
Call id(2), args( Qubit(2), )
Variable(1, Integer) = Store Integer(3)
Variable(2, Integer) = Store Integer(0)
Variable(2, Integer) = Store Integer(1)
Variable(2, Integer) = Store Integer(2)
Variable(2, Integer) = Store Integer(3)
Jump(1)
Block 1: Block:
Variable(2, Boolean) = Icmp Slt, Variable(1, Integer), Integer(3)
Branch Variable(2, Boolean), 3, 2
Block 2: Block:
Call id(3), args( Integer(0), Tag(0, 3), )
Return
Block 3: Block:
Variable(3, Qubit) = Index Array(0), Variable(1, Integer)
Variable(4, Qubit) = Store Variable(3, Qubit)
Call id(2), args( Variable(4, Qubit), )
Variable(5, Integer) = Add Variable(1, Integer), Integer(1)
Variable(1, Integer) = Store Variable(5, Integer)
Jump(1)
config: Config:
capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | StaticSizedArrays)
num_qubits: 3
num_results: 0
tags:
[0]: 0_t
array_literals:
[0]: [Qubit(0), Qubit(1), Qubit(2)]
"#]].assert_eq(&program.to_string());
}

Expand Down Expand Up @@ -829,10 +832,6 @@ fn for_loop_over_qubits_unrolled() {
Variable(1, Integer) = Store Integer(2)
Call id(2), args( Qubit(2), )
Variable(1, Integer) = Store Integer(3)
Variable(2, Integer) = Store Integer(0)
Variable(2, Integer) = Store Integer(1)
Variable(2, Integer) = Store Integer(2)
Variable(2, Integer) = Store Integer(3)
Call id(3), args( Integer(0), Tag(0, 3), )
Return"#]],
);
Expand Down Expand Up @@ -1596,9 +1595,6 @@ fn result_array_index_range_in_for_loop_unrolled() {
Block 3: Block:
Variable(3, Integer) = Store Integer(2)
Variable(9, Integer) = Store Variable(2, Integer)
Variable(10, Integer) = Store Integer(0)
Variable(10, Integer) = Store Integer(1)
Variable(10, Integer) = Store Integer(2)
Call id(4), args( Variable(9, Integer), Tag(0, 3), )
Return
Block 4: Block:
Expand Down
4 changes: 0 additions & 4 deletions source/compiler/qsc_partial_eval/src/tests/qubits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,10 +303,6 @@ fn qubit_array_allocation_and_access() {
Call id(2), args( Qubit(0), )
Call id(2), args( Qubit(1), )
Call id(2), args( Qubit(2), )
Variable(1, Integer) = Store Integer(0)
Variable(1, Integer) = Store Integer(1)
Variable(1, Integer) = Store Integer(2)
Variable(1, Integer) = Store Integer(3)
Call id(3), args( Integer(0), Tag(0, 3), )
Return"#]],
);
Expand Down
2 changes: 1 addition & 1 deletion source/compiler/qsc_rca/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,7 @@ impl RuntimeFeatureFlags {
capabilities |= TargetCapabilityFlags::FloatingPointComputations;
}
if self.contains(RuntimeFeatureFlags::UseOfDynamicQubit) {
capabilities |= TargetCapabilityFlags::HigherLevelConstructs;
capabilities |= TargetCapabilityFlags::StaticSizedArrays;
}
if self.contains(RuntimeFeatureFlags::UseOfDynamicBigInt) {
capabilities |= TargetCapabilityFlags::HigherLevelConstructs;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,72 +25,72 @@ block_0:
block_1:
br label %block_2
block_2:
%var_39 = phi i64 [1, %block_0], [3, %block_1]
%var_38 = phi i64 [10, %block_0], [8, %block_1]
%var_37 = phi i64 [0, %block_0], [5, %block_1]
%var_36 = phi i64 [0, %block_0], [1, %block_1]
%var_38 = phi i64 [1, %block_0], [3, %block_1]
%var_37 = phi i64 [10, %block_0], [8, %block_1]
%var_36 = phi i64 [0, %block_0], [5, %block_1]
%var_35 = phi i64 [0, %block_0], [1, %block_1]
%var_10 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 1 to %Result*))
br i1 %var_10, label %block_3, label %block_4
block_3:
%var_12 = add i64 %var_36, 1
%var_13 = add i64 %var_37, 5
%var_14 = sub i64 %var_38, 2
%var_15 = mul i64 %var_39, 3
%var_12 = add i64 %var_35, 1
%var_13 = add i64 %var_36, 5
%var_14 = sub i64 %var_37, 2
%var_15 = mul i64 %var_38, 3
br label %block_4
block_4:
%var_43 = phi i64 [%var_39, %block_2], [%var_15, %block_3]
%var_42 = phi i64 [%var_38, %block_2], [%var_14, %block_3]
%var_41 = phi i64 [%var_37, %block_2], [%var_13, %block_3]
%var_40 = phi i64 [%var_36, %block_2], [%var_12, %block_3]
%var_42 = phi i64 [%var_38, %block_2], [%var_15, %block_3]
%var_41 = phi i64 [%var_37, %block_2], [%var_14, %block_3]
%var_40 = phi i64 [%var_36, %block_2], [%var_13, %block_3]
%var_39 = phi i64 [%var_35, %block_2], [%var_12, %block_3]
%var_16 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 2 to %Result*))
br i1 %var_16, label %block_5, label %block_6
block_5:
%var_18 = add i64 %var_40, 1
%var_19 = add i64 %var_41, 5
%var_20 = sub i64 %var_42, 2
%var_21 = mul i64 %var_43, 3
%var_18 = add i64 %var_39, 1
%var_19 = add i64 %var_40, 5
%var_20 = sub i64 %var_41, 2
%var_21 = mul i64 %var_42, 3
br label %block_6
block_6:
%var_47 = phi i64 [%var_43, %block_4], [%var_21, %block_5]
%var_46 = phi i64 [%var_42, %block_4], [%var_20, %block_5]
%var_45 = phi i64 [%var_41, %block_4], [%var_19, %block_5]
%var_44 = phi i64 [%var_40, %block_4], [%var_18, %block_5]
%var_46 = phi i64 [%var_42, %block_4], [%var_21, %block_5]
%var_45 = phi i64 [%var_41, %block_4], [%var_20, %block_5]
%var_44 = phi i64 [%var_40, %block_4], [%var_19, %block_5]
%var_43 = phi i64 [%var_39, %block_4], [%var_18, %block_5]
%var_22 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 3 to %Result*))
br i1 %var_22, label %block_7, label %block_8
block_7:
%var_24 = add i64 %var_44, 1
%var_25 = add i64 %var_45, 5
%var_26 = sub i64 %var_46, 2
%var_27 = mul i64 %var_47, 3
%var_24 = add i64 %var_43, 1
%var_25 = add i64 %var_44, 5
%var_26 = sub i64 %var_45, 2
%var_27 = mul i64 %var_46, 3
br label %block_8
block_8:
%var_51 = phi i64 [%var_47, %block_6], [%var_27, %block_7]
%var_50 = phi i64 [%var_46, %block_6], [%var_26, %block_7]
%var_49 = phi i64 [%var_45, %block_6], [%var_25, %block_7]
%var_48 = phi i64 [%var_44, %block_6], [%var_24, %block_7]
%var_50 = phi i64 [%var_46, %block_6], [%var_27, %block_7]
%var_49 = phi i64 [%var_45, %block_6], [%var_26, %block_7]
%var_48 = phi i64 [%var_44, %block_6], [%var_25, %block_7]
%var_47 = phi i64 [%var_43, %block_6], [%var_24, %block_7]
%var_28 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 4 to %Result*))
br i1 %var_28, label %block_9, label %block_10
block_9:
%var_30 = add i64 %var_48, 1
%var_31 = add i64 %var_49, 5
%var_32 = sub i64 %var_50, 2
%var_33 = mul i64 %var_51, 3
%var_30 = add i64 %var_47, 1
%var_31 = add i64 %var_48, 5
%var_32 = sub i64 %var_49, 2
%var_33 = mul i64 %var_50, 3
br label %block_10
block_10:
%var_55 = phi i64 [%var_51, %block_8], [%var_33, %block_9]
%var_54 = phi i64 [%var_50, %block_8], [%var_32, %block_9]
%var_53 = phi i64 [%var_49, %block_8], [%var_31, %block_9]
%var_52 = phi i64 [%var_48, %block_8], [%var_30, %block_9]
%var_54 = phi i64 [%var_50, %block_8], [%var_33, %block_9]
%var_53 = phi i64 [%var_49, %block_8], [%var_32, %block_9]
%var_52 = phi i64 [%var_48, %block_8], [%var_31, %block_9]
%var_51 = phi i64 [%var_47, %block_8], [%var_30, %block_9]
call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 0 to %Qubit*))
call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 1 to %Qubit*))
call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 2 to %Qubit*))
call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 3 to %Qubit*))
call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 4 to %Qubit*))
call void @__quantum__rt__tuple_record_output(i64 4, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
call void @__quantum__rt__int_record_output(i64 %var_52, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0))
call void @__quantum__rt__int_record_output(i64 %var_53, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i64 0, i64 0))
call void @__quantum__rt__int_record_output(i64 %var_54, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @3, i64 0, i64 0))
call void @__quantum__rt__int_record_output(i64 %var_55, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @4, i64 0, i64 0))
call void @__quantum__rt__int_record_output(i64 %var_51, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0))
call void @__quantum__rt__int_record_output(i64 %var_52, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i64 0, i64 0))
call void @__quantum__rt__int_record_output(i64 %var_53, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @3, i64 0, i64 0))
call void @__quantum__rt__int_record_output(i64 %var_54, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @4, i64 0, i64 0))
ret i64 0
}

Expand Down
Loading