// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
mod arithmetic;
mod arrays;
mod canon;
mod convert;
mod core;
mod diagnostics;
mod intrinsic;
mod logical;
mod math;
mod measurement;
mod openqasm;
mod state_preparation;
mod table_lookup;
use indoc::indoc;
use qsc::{
Backend, LanguageFeatures, PackageType, SourceMap, SparseSim,
interpret::{self, GenericReceiver, Interpreter, Value},
target::Profile,
};
/// # Panics
///
/// Will panic if compilation fails or the result is not the same as expected.
/// NOTE: Floating point numbers in tuples are compared taking precision into
/// account so that results of calculations can also be compared.
pub fn test_expression(expr: &str, expected: &Value) -> String {
test_expression_with_lib(expr, "", expected)
}
pub fn test_expression_fails(expr: &str) -> String {
test_expression_fails_with_lib_and_profile_and_sim(
expr,
"",
Profile::Unrestricted,
&mut SparseSim::default(),
)
}
pub fn test_expression_with_lib(expr: &str, lib: &str, expected: &Value) -> String {
test_expression_with_lib_and_profile(expr, lib, Profile::Unrestricted, expected)
}
pub fn test_expression_with_lib_and_profile(
expr: &str,
lib: &str,
profile: Profile,
expected: &Value,
) -> String {
let mut sim = SparseSim::default();
test_expression_with_lib_and_profile_and_sim(expr, lib, profile, &mut sim, expected)
}
pub fn test_expression_with_lib_and_profile_and_sim(
expr: &str,
lib: &str,
profile: Profile,
sim: &mut impl Backend,
expected: &Value,
) -> String {
let mut stdout = vec![];
let mut out = GenericReceiver::new(&mut stdout);
let sources = SourceMap::new([("test".into(), lib.into())], Some(expr.into()));
let (std_id, store) = qsc::compile::package_store_with_stdlib(profile.into());
let mut interpreter = Interpreter::new(
sources,
PackageType::Exe,
profile.into(),
LanguageFeatures::default(),
store,
&[(std_id, None)],
)
.expect("test should compile");
let result = interpreter
.eval_entry_with_sim(sim, &mut out)
.expect("test should run successfully");
match (&expected, result) {
(&Value::Tuple(tup1, _), Value::Tuple(tup2, _)) if tup1.len() == tup2.len() => {
// If both values are tuples of the same length, we crack them open and compare elements
for (value1, value2) in tup1.iter().zip(tup2.iter()) {
if let (Value::Double(double1), Value::Double(double2)) = (value1, value2) {
// If both elements are doubles, we use approximate comparison
assert_doubles_almost_equal(*double1, *double2);
} else {
assert_eq!(value1, value2);
}
}
}
(&Value::Double(double1), Value::Double(double2)) => {
assert_doubles_almost_equal(*double1, double2);
}
(&expected, result) => assert_eq!(expected, &result),
}
String::from_utf8(stdout).expect("stdout should be valid utf8")
}
pub fn test_expression_fails_with_lib_and_profile_and_sim(
expr: &str,
lib: &str,
profile: Profile,
sim: &mut impl Backend,
) -> String {
let mut stdout = vec![];
let mut out = GenericReceiver::new(&mut stdout);
let sources = SourceMap::new([("test".into(), lib.into())], Some(expr.into()));
let (std_id, store) = qsc::compile::package_store_with_stdlib(profile.into());
let mut interpreter = Interpreter::new(
sources,
PackageType::Exe,
profile.into(),
LanguageFeatures::default(),
store,
&[(std_id, None)],
)
.expect("test should compile");
let result = interpreter
.eval_entry_with_sim(sim, &mut out)
.expect_err("test should run successfully");
assert!(
result.len() == 1,
"Expected a single error, got {:?}",
result.len()
);
let interpret::Error::Eval(err) = &result[0] else {
panic!("Expected an Eval error, got {:?}", result[0]);
};
err.error().error().to_string()
}
/// # Panics
///
/// Will panic if f64 values are significantly different.
fn assert_doubles_almost_equal(val1: f64, val2: f64) {
let val1_abs = val1.abs();
let val2_abs = val2.abs();
if val1_abs < f64::MIN_POSITIVE && val2_abs < f64::MIN_POSITIVE {
// Note, that f64::MIN_POSITIVE is not the smallest representable positive number.
return;
}
assert!(
((val1 - val2).abs() / (val1_abs + val2_abs)) < 1e-15,
"Significant difference between expected and actual values: val1={val1}, val2={val2}."
);
}
//
// Core namespace
//
#[test]
fn check_repeated() {
test_expression("Repeated(Zero, 0)", &Value::Array(vec![].into()));
test_expression(
"Repeated(One, 1)",
&Value::Array(vec![Value::RESULT_ONE].into()),
);
test_expression(
"Repeated(1, 2)",
&Value::Array(vec![Value::Int(1), Value::Int(1)].into()),
);
test_expression(
"Repeated(true, 3)",
&Value::Array(vec![Value::Bool(true), Value::Bool(true), Value::Bool(true)].into()),
);
}
#[test]
fn check_exp_with_cnot() {
// This decomposition only holds if the magnitude of the angle used in Exp is correct and if the
// sign convention between Rx, Rz, and Exp is consistent.
test_expression(
indoc! {r#"{
import Std.Diagnostics.*;
import Std.Math.*;
use (aux, control, target) = (Qubit(), Qubit(), Qubit());
within {
H(aux);
CNOT(aux, control);
CNOT(aux, target);
}
apply {
let theta = PI() / 4.0;
Rx(-2.0 * theta, target);
Rz(-2.0 * theta, control);
Adjoint Exp([PauliZ, PauliX], theta, [control, target]);
Adjoint CNOT(control, target);
}
CheckAllZero([aux, control, target])
}"#},
&Value::Bool(true),
);
}
#[test]
fn check_exp_with_swap() {
// This decomposition only holds if the magnitude of the angle used in Exp is correct.
test_expression(
indoc! {r#"{
import Std.Diagnostics.*;
import Std.Math.*;
use (aux, qs) = (Qubit(), Qubit[2]);
within {
H(aux);
CNOT(aux, qs[0]);
CNOT(aux, qs[1]);
}
apply {
let theta = PI() / 4.0;
Exp([PauliX, PauliX], theta, qs);
Exp([PauliY, PauliY], theta, qs);
Exp([PauliZ, PauliZ], theta, qs);
Adjoint SWAP(qs[0], qs[1]);
}
CheckAllZero([aux] + qs)
}"#},
&Value::Bool(true),
);
}
#[test]
fn check_base_profile_measure_resets_aux_qubits() {
test_expression_with_lib_and_profile(
indoc! {"{
use q = Qubit();
X(q);
let result = M(q);
Reset(q);
result
}"},
"",
Profile::Base,
&Value::RESULT_ONE,
);
}
// just tests a single case of the stdlib reexports for the modern api,
// to ensure that reexporting functionality doesn't break
#[test]
fn stdlib_reexport_single_case() {
test_expression(
r#" {
import Std.Arrays.Count;
}"#,
&Value::Tuple(vec![].into(), None),
);
}microsoft/qdk
Publicmirrored fromhttps://github.com/microsoft/qdkAvailable
library/src/tests.rs
266lines · modepreview