Skip to content

Commit

Permalink
Partial evaluation support for assignment expressions and multiple ar…
Browse files Browse the repository at this point in the history
…ray related expressions (#1480)

This change adds support in partial evaluation for the following
expressions:
- Assignment.
- Array repeat.
- Array in-place concatenation.
- Array in-place update of value at index.
- Array access to value at index.

---------

Co-authored-by: Ian Davis <[email protected]>
Co-authored-by: Stefan J. Wernli <[email protected]>
  • Loading branch information
3 people authored May 6, 2024
1 parent 530fc4b commit 2cd8357
Show file tree
Hide file tree
Showing 12 changed files with 2,225 additions and 143 deletions.
11 changes: 8 additions & 3 deletions compiler/qsc_eval/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ impl Default for Env {

impl Env {
#[must_use]
fn get(&self, id: LocalVarId) -> Option<&Variable> {
pub fn get(&self, id: LocalVarId) -> Option<&Variable> {
self.0.iter().rev().find_map(|scope| scope.bindings.get(id))
}

Expand Down Expand Up @@ -406,6 +406,13 @@ impl Env {
pub fn len(&self) -> usize {
self.0.len()
}

pub fn update_variable_in_top_frame(&mut self, local_var_id: LocalVarId, value: Value) {
let variable = self
.get_mut(local_var_id)
.expect("local variable is not present");
variable.value = value;
}
}

#[derive(Default)]
Expand Down Expand Up @@ -541,7 +548,6 @@ impl State {
step: StepAction,
) -> Result<StepResult, (Error, Vec<Frame>)> {
let current_frame = self.call_stack.len();

while !self.exec_graph_stack.is_empty() {
let exec_graph = self
.exec_graph_stack
Expand Down Expand Up @@ -686,7 +692,6 @@ impl State {
) -> Result<(), Error> {
let expr = globals.get_expr((self.package, expr).into());
self.current_span = expr.span;

match &expr.kind {
ExprKind::Array(arr) => self.eval_arr(arr.len()),
ExprKind::ArrayLit(arr) => self.eval_arr_lit(arr, globals),
Expand Down
24 changes: 22 additions & 2 deletions compiler/qsc_eval/src/val.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,10 @@ impl From<usize> for Result {
pub struct Qubit(pub usize);

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Var(pub usize);
pub struct Var {
pub id: usize,
pub ty: VarTy,
}

impl Display for Value {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Expand Down Expand Up @@ -147,7 +150,24 @@ impl Display for Value {
}
write!(f, ")")
}
Value::Var(var) => write!(f, "Var{}", (var.0)),
Value::Var(var) => write!(f, "Var({}, {})", var.id, var.ty),
}
}
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum VarTy {
Boolean,
Integer,
Double,
}

impl Display for VarTy {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Boolean => write!(f, "Boolean"),
Self::Integer => write!(f, "Integer"),
Self::Double => write!(f, "Double"),
}
}
}
Expand Down
29 changes: 21 additions & 8 deletions compiler/qsc_partial_eval/src/evaluation_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use qsc_fir::fir::{LocalItemId, LocalVarId, PackageId};
use qsc_rca::{RuntimeKind, ValueKind};
use qsc_rir::rir::BlockId;
use rustc_hash::FxHashMap;
use std::collections::hash_map::Entry;

/// Struct that keeps track of the active RIR blocks (where RIR instructions are added) and the active scopes (which
/// correspond to the Q#'s program call stack).
Expand Down Expand Up @@ -95,10 +96,10 @@ pub struct Scope {
pub args_value_kind: Vec<ValueKind>,
/// The classical environment of the callable, which holds values corresponding to local variables.
pub env: Env,
/// Map that holds the values of local variables.
pub hybrid_vars: FxHashMap<LocalVarId, Value>,
/// Number of currently active blocks (starting from where this scope was created).
active_block_count: usize,
/// Map that holds the values of local variables.
hybrid_vars: FxHashMap<LocalVarId, Value>,
}

impl Scope {
Expand Down Expand Up @@ -155,11 +156,20 @@ impl Scope {
}
}

/// Gets the value of a (hybrid) local variable.
pub fn get_local_var_value(&self, local_var_id: LocalVarId) -> &Value {
/// Gets the value of a hybrid local variable.
pub fn get_classical_local_value(&self, local_var_id: LocalVarId) -> &Value {
&self
.env
.get(local_var_id)
.expect("local classcial variable value does not exist")
.value
}

/// Gets the value of a hybrid local variable.
pub fn get_hybrid_local_value(&self, local_var_id: LocalVarId) -> &Value {
self.hybrid_vars
.get(&local_var_id)
.expect("local variable value does not exist")
.expect("local hybrid variable value does not exist")
}

/// Determines whether we are currently evaluating a branch within the scope.
Expand All @@ -174,9 +184,12 @@ impl Scope {
self.env.len() == 1
}

/// Inserts the value of a local variable into the hybrid variables map.
pub fn insert_local_var_value(&mut self, local_var_id: LocalVarId, value: Value) {
self.hybrid_vars.insert(local_var_id, value);
/// Updates the value of a hybrid local variable.
pub fn update_hybrid_local_value(&mut self, local_var_id: LocalVarId, value: Value) {
let Entry::Occupied(mut occupied) = self.hybrid_vars.entry(local_var_id) else {
panic!("local variable to update does not exist");
};
occupied.insert(value);
}
}

Expand Down
Loading

0 comments on commit 2cd8357

Please sign in to comment.