Skip to content

Commit

Permalink
Partial Evaluation Output Recording support (#1387)
Browse files Browse the repository at this point in the history
This adds logic to insert output recording calls for recognized types
into RIR programs generated by partial evaluation.
  • Loading branch information
swernli authored Apr 17, 2024
1 parent 2963b42 commit ad4578e
Show file tree
Hide file tree
Showing 18 changed files with 2,907 additions and 1,708 deletions.
81 changes: 79 additions & 2 deletions compiler/qsc/src/interpret/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -727,14 +727,15 @@ mod given_interpreter {
open Microsoft.Quantum.Math;
open QIR.Intrinsic;
@EntryPoint()
operation Main() : Unit {
operation Main() : Result {
use q = Qubit();
let pi_over_2 = 4.0 / 2.0;
__quantum__qis__rz__body(pi_over_2, q);
mutable some_angle = ArcSin(0.0);
__quantum__qis__rz__body(some_angle, q);
set some_angle = ArcCos(-1.0) / PI();
__quantum__qis__rz__body(some_angle, q);
__quantum__qis__mresetz__body(q)
}
}"#
},
Expand All @@ -750,12 +751,88 @@ mod given_interpreter {
call void @__quantum__qis__rz__body(double 2.0, %Qubit* inttoptr (i64 0 to %Qubit*))
call void @__quantum__qis__rz__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*))
call void @__quantum__qis__rz__body(double 1.0, %Qubit* inttoptr (i64 0 to %Qubit*))
call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
ret void
}
declare void @__quantum__qis__rz__body(double, %Qubit*)
attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="0" }
declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
declare void @__quantum__rt__result_record_output(%Result*, i8*)
attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "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}
"#]]
.assert_eq(&res);
}

#[test]
fn adaptive_qirgen_nested_output_types() {
let mut interpreter = Interpreter::new(
true,
SourceMap::default(),
PackageType::Lib,
TargetCapabilityFlags::Adaptive,
LanguageFeatures::default(),
)
.expect("interpreter should be created");
let (result, output) = line(
&mut interpreter,
indoc! {r#"
namespace Test {
open QIR.Intrinsic;
@EntryPoint()
operation Main() : (Result, (Bool, Bool)) {
use q = Qubit();
let r = __quantum__qis__mresetz__body(q);
(r, (r == One, r == Zero))
}
}"#
},
);
is_only_value(&result, &output, &Value::unit());
let res = interpreter.qirgen("Test.Main()").expect("expected success");
expect![[r#"
%Result = type opaque
%Qubit = type opaque
define void @ENTRYPOINT__main() #0 {
block_0:
call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
%var_0 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 0 to %Result*))
%var_1 = icmp eq i1 %var_0, true
%var_2 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 0 to %Result*))
%var_3 = icmp eq i1 %var_2, false
call void @__quantum__rt__tuple_record_output(i64 2, i8* null)
call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
call void @__quantum__rt__tuple_record_output(i64 2, i8* null)
call void @__quantum__rt__bool_record_output(i1 %var_1, i8* null)
call void @__quantum__rt__bool_record_output(i1 %var_3, i8* null)
ret void
}
declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
declare i1 @__quantum__qis__read_result__body(%Result*)
declare void @__quantum__rt__tuple_record_output(i64, i8*)
declare void @__quantum__rt__result_record_output(%Result*, i8*)
declare void @__quantum__rt__bool_record_output(i1, i8*)
attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
attributes #1 = { "irreversible" }
; module flags
Expand Down
3 changes: 1 addition & 2 deletions compiler/qsc_eval/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

#![allow(clippy::needless_raw_string_hashes)]

use core::panic;
use std::rc::Rc;

use crate::{
Expand Down Expand Up @@ -173,7 +172,7 @@ fn check_partial_eval_stmt(
&mut GenericReceiver::new(&mut out),
) {
Ok(_) => {}
Err(err) => panic!("Unexpected error: {:?}", err),
Err(err) => panic!("Unexpected error: {err:?}"),
}
}

Expand Down
16 changes: 15 additions & 1 deletion compiler/qsc_partial_eval/src/evaluation_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ pub struct Scope {
pub callable: Option<(LocalItemId, FunctorApp)>,
pub args_runtime_properties: Vec<ValueKind>,
pub env: Env,
last_expr: Option<ExprId>,
hybrid_exprs: FxHashMap<ExprId, Value>,
hybrid_vars: FxHashMap<LocalVarId, Value>,
}
Expand All @@ -87,12 +88,14 @@ impl Scope {
callable,
args_runtime_properties,
env: Env::default(),
last_expr: None,
hybrid_exprs: FxHashMap::default(),
hybrid_vars: FxHashMap::default(),
}
}

pub fn get_expr_value(&self, expr_id: ExprId) -> &Value {
// Potential candidate for removal if only the last expression value is needed.
pub fn _get_expr_value(&self, expr_id: ExprId) -> &Value {
self.hybrid_exprs
.get(&expr_id)
.expect("expression value does not exist")
Expand All @@ -105,10 +108,21 @@ impl Scope {
}

pub fn insert_expr_value(&mut self, expr_id: ExprId, value: Value) {
self.last_expr = Some(expr_id);
self.hybrid_exprs.insert(expr_id, value);
}

pub fn insert_local_var_value(&mut self, local_var_id: LocalVarId, value: Value) {
self.hybrid_vars.insert(local_var_id, value);
}

pub fn clear_last_expr(&mut self) {
self.last_expr = None;
}

pub fn last_expr_value(&self) -> Value {
self.last_expr
.and_then(|expr_id| self.hybrid_exprs.get(&expr_id))
.map_or_else(Value::unit, Clone::clone)
}
}
Loading

0 comments on commit ad4578e

Please sign in to comment.