Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into uh/oplint
Browse files Browse the repository at this point in the history
  • Loading branch information
Manvi-Agrawal committed Jun 7, 2024
2 parents 3944165 + dec8db0 commit 031b110
Show file tree
Hide file tree
Showing 194 changed files with 4,675 additions and 247 deletions.
2 changes: 2 additions & 0 deletions build.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,8 @@ def use_python_env(folder):
f.startswith("sample.")
or f.startswith("azure_submission.")
or f.startswith("circuits.")
or f.startswith("iterative_phase_estimation.")
or f.startswith("repeat_until_success.")
)
]
python_bin = use_python_env(samples_src)
Expand Down
10 changes: 9 additions & 1 deletion compiler/qsc_data_structures/src/index_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,15 @@ impl<K, V: Clone> Clone for IndexMap<K, V> {
impl<K, V: Debug> Debug for IndexMap<K, V> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_struct("IndexMap")
.field("values", &self.values)
.field(
"values",
&self
.values
.iter()
.enumerate()
.filter_map(|(k, v)| v.as_ref().map(|val| format!("{k:?}: {val:?}")))
.collect::<Vec<_>>(),
)
.finish()
}
}
Expand Down
13 changes: 10 additions & 3 deletions compiler/qsc_data_structures/src/span.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,21 @@ pub struct Span {
}

impl Span {
/// Returns true if the position is within the range.
/// Returns true if the position is within the span. Meaning it is in the
/// right open interval `[self.lo, self.hi)`.
#[must_use]
pub fn contains(&self, offset: u32) -> bool {
(self.lo..self.hi).contains(&offset)
}

/// Intersect `range` with this range and returns a new range or `None`
/// if the ranges have no overlap.
/// Returns true if the position is in the closed interval `[self.lo, self.hi]`.
#[must_use]
pub fn touches(&self, offset: u32) -> bool {
(self.lo..=self.hi).contains(&offset)
}

/// Intersect `other` with `self` and returns a new `Span` or `None`
/// if the spans have no overlap.
#[must_use]
pub fn intersection(&self, other: &Self) -> Option<Self> {
let lo = self.lo.max(other.lo);
Expand Down
40 changes: 40 additions & 0 deletions compiler/qsc_eval/src/intrinsic/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,46 @@ fn dump_register_target_in_minus_with_other_in_one() {
);
}

#[test]
fn dump_register_all_qubits_normalized_is_same_as_dump_machine() {
check_intrinsic_output(
"",
indoc! {
"{
open Microsoft.Quantum.Diagnostics;
use qs = Qubit[2];
let alpha = -4.20025;
let beta = 2.04776;
let gamma = -5.47097;
within{
Ry(alpha, qs[0]);
Ry(beta, qs[1]);
CNOT(qs[0], qs[1]);
Ry(gamma, qs[1]);
}
apply{
DumpRegister(qs);
DumpMachine();
}
}"
},
&expect![[r#"
STATE:
|00⟩: 0.0709+0.0000𝑖
|01⟩: 0.5000+0.0000𝑖
|10⟩: 0.5000+0.0000𝑖
|11⟩: 0.7036+0.0000𝑖
STATE:
|00⟩: 0.0709+0.0000𝑖
|01⟩: 0.5000+0.0000𝑖
|10⟩: 0.5000+0.0000𝑖
|11⟩: 0.7036+0.0000𝑖
"#]],
);
}

#[test]
fn message() {
check_intrinsic_output(
Expand Down
4 changes: 2 additions & 2 deletions compiler/qsc_eval/src/intrinsic/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,15 @@ fn collect_split_state(
// When capturing the amplitude for the dump state, we must divide out the amplitude for the other
// state, and vice-versa below.
let amplitude = curr_val / other_val;
let norm = amplitude.norm();
let norm = amplitude.norm().powi(2);
if !norm.is_nearly_zero() {
entry.insert(amplitude);
dump_norm += norm;
}
}
if let Entry::Vacant(entry) = other_state.entry(other_label) {
let amplitude = curr_val / dump_val;
let norm = amplitude.norm();
let norm = amplitude.norm().powi(2);
if !norm.is_nearly_zero() {
entry.insert(amplitude);
}
Expand Down
16 changes: 2 additions & 14 deletions compiler/qsc_eval/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use num_bigint::BigInt;
use output::Receiver;
use qsc_data_structures::{functors::FunctorApp, index_map::IndexMap, span::Span};
use qsc_fir::fir::{
self, BinOp, CallableImpl, ExecGraphNode, Expr, ExprId, ExprKind, Field, Functor, Global, Lit,
self, BinOp, CallableImpl, ExecGraphNode, Expr, ExprId, ExprKind, Field, Global, Lit,
LocalItemId, LocalVarId, PackageId, PackageStoreLookup, PatId, PatKind, PrimField, Res, StmtId,
StoreItemId, StringComponent, UnOp,
};
Expand All @@ -52,6 +52,7 @@ use std::{
rc::Rc,
};
use thiserror::Error;
use val::update_functor_app;

#[derive(Clone, Debug, Diagnostic, Error)]
pub enum Error {
Expand Down Expand Up @@ -1816,19 +1817,6 @@ fn eval_binop_xorb(lhs_val: Value, rhs_val: Value) -> Value {
}
}

fn update_functor_app(functor: Functor, app: FunctorApp) -> FunctorApp {
match functor {
Functor::Adj => FunctorApp {
adjoint: !app.adjoint,
controlled: app.controlled,
},
Functor::Ctl => FunctorApp {
adjoint: app.adjoint,
controlled: app.controlled + 1,
},
}
}

fn follow_field_path(mut value: Value, path: &[usize]) -> Option<Value> {
for &index in path {
let Value::Tuple(items) = value else {
Expand Down
16 changes: 15 additions & 1 deletion compiler/qsc_eval/src/val.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

use num_bigint::BigInt;
use qsc_data_structures::{display::join, functors::FunctorApp};
use qsc_fir::fir::{Pauli, StoreItemId};
use qsc_fir::fir::{Functor, Pauli, StoreItemId};
use std::{
fmt::{self, Display, Formatter},
rc::Rc,
Expand Down Expand Up @@ -476,3 +476,17 @@ pub fn update_index_range(
}
Ok(Value::Array(values.into()))
}

#[must_use]
pub fn update_functor_app(functor: Functor, app: FunctorApp) -> FunctorApp {
match functor {
Functor::Adj => FunctorApp {
adjoint: !app.adjoint,
controlled: app.controlled,
},
Functor::Ctl => FunctorApp {
adjoint: app.adjoint,
controlled: app.controlled + 1,
},
}
}
46 changes: 41 additions & 5 deletions compiler/qsc_frontend/src/compile/preprocess.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

use core::str::FromStr;
use qsc_ast::{
ast::{Attr, ExprKind, ItemKind, Namespace, Stmt, StmtKind},
ast::{Attr, ExprKind, ItemKind, Namespace, Stmt, StmtKind, UnOp},
mut_visit::MutVisitor,
};
use qsc_hir::hir;
Expand Down Expand Up @@ -131,29 +131,65 @@ fn matches_config(attrs: &[Box<Attr>], capabilities: TargetCapabilityFlags) -> b
return true;
}
let mut found_capabilities = TargetCapabilityFlags::empty();
let mut disallowed_capabilities = TargetCapabilityFlags::empty();
let mut base = false;
let mut not_base = false;

// When checking attributes, anything we don't recognize (invalid form or invalid capability) gets
// left in the compilation by returning true. This ensures that later compilation steps, specifically lowering
// from AST to HIR, can check the attributes and return errors as appropriate.
for attr in attrs {
if let ExprKind::Paren(inner) = attr.arg.kind.as_ref() {
match inner.kind.as_ref() {
ExprKind::Path(path) => {
if let Ok(capability) = TargetCapabilityFlags::from_str(path.name.name.as_ref())
{
if capability.is_empty() {
base = true;
}
found_capabilities |= capability;
} else {
return true; // Unknown capability, so we assume it matches
}
}
ExprKind::UnOp(UnOp::NotL, inner) => {
if let ExprKind::Path(path) = inner.kind.as_ref() {
if let Ok(capability) =
TargetCapabilityFlags::from_str(path.name.name.as_ref())
{
if capability.is_empty() {
not_base = true;
}
disallowed_capabilities |= capability;
} else {
return true; // Unknown capability, so we assume it matches
}
} else {
return true; // Unknown config attribute, so we assume it matches
}
}
_ => return true, // Unknown config attribute, so we assume it matches
}
} else {
// Something other than a parenthesized expression, so we assume it matches
return true;
}
}
if found_capabilities == TargetCapabilityFlags::empty() {
// There was at least one config attribute, but it was None
// Therefore, we only match if there are no capabilities
return capabilities == TargetCapabilityFlags::empty();
if found_capabilities.is_empty() && disallowed_capabilities.is_empty() {
if not_base && !base {
// There was at least one config attribute, but it was "not Base" so
// ensure that the capabilities are not empty.
return capabilities != TargetCapabilityFlags::empty();
} else if base && !not_base {
// There was at least one config attribute, but it was Base
// Therefore, we only match if there are no capabilities
return capabilities == TargetCapabilityFlags::empty();
}

// The config specified both "Base" and "not Base" which is a contradiction, but we
// drop the item in this case.
return false;
}
capabilities.contains(found_capabilities)
&& (disallowed_capabilities.is_empty() || !capabilities.contains(disallowed_capabilities))
}
28 changes: 20 additions & 8 deletions compiler/qsc_frontend/src/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,14 +226,26 @@ impl With<'_> {
}
},
Ok(hir::Attr::Config) => {
if !matches!(attr.arg.kind.as_ref(), ast::ExprKind::Paren(inner)
if matches!(inner.kind.as_ref(), ast::ExprKind::Path(path)
if TargetCapabilityFlags::from_str(path.name.name.as_ref()).is_ok()))
{
self.lowerer.errors.push(Error::InvalidAttrArgs(
"runtime capability".to_string(),
attr.arg.span,
));
match &*attr.arg.kind {
// @Config(Capability)
ast::ExprKind::Paren(inner)
if matches!(inner.kind.as_ref(), ast::ExprKind::Path(path)
if TargetCapabilityFlags::from_str(path.name.name.as_ref()).is_ok()) => {}

// @Config(not Capability)
ast::ExprKind::Paren(inner)
if matches!(inner.kind.as_ref(), ast::ExprKind::UnOp(ast::UnOp::NotL, inner)
if matches!(inner.kind.as_ref(), ast::ExprKind::Path(path)
if TargetCapabilityFlags::from_str(path.as_ref().name.name.as_ref()).is_ok())) =>
{}

// Any other form is not valid so generates an error.
_ => {
self.lowerer.errors.push(Error::InvalidAttrArgs(
"runtime capability".to_string(),
attr.arg.span,
));
}
}
None
}
Expand Down
Loading

0 comments on commit 031b110

Please sign in to comment.