microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
minestarks/circuit-disable-tracing

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

library/src/tests.rs

265lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4mod arithmetic;
5mod arrays;
6mod canon;
7mod convert;
8mod core;
9mod diagnostics;
10mod intrinsic;
11mod logical;
12mod math;
13mod measurement;
14mod state_preparation;
15mod table_lookup;
16
17use indoc::indoc;
18use qsc::{
19 Backend, LanguageFeatures, PackageType, SourceMap, SparseSim,
20 interpret::{self, GenericReceiver, Interpreter, Result, Value},
21 target::Profile,
22};
23
24/// # Panics
25///
26/// Will panic if compilation fails or the result is not the same as expected.
27/// NOTE: Floating point numbers in tuples are compared taking precision into
28/// account so that results of calculations can also be compared.
29pub fn test_expression(expr: &str, expected: &Value) -> String {
30 test_expression_with_lib(expr, "", expected)
31}
32
33pub fn test_expression_fails(expr: &str) -> String {
34 test_expression_fails_with_lib_and_profile_and_sim(
35 expr,
36 "",
37 Profile::Unrestricted,
38 &mut SparseSim::default(),
39 )
40}
41
42pub fn test_expression_with_lib(expr: &str, lib: &str, expected: &Value) -> String {
43 test_expression_with_lib_and_profile(expr, lib, Profile::Unrestricted, expected)
44}
45
46pub fn test_expression_with_lib_and_profile(
47 expr: &str,
48 lib: &str,
49 profile: Profile,
50 expected: &Value,
51) -> String {
52 let mut sim = SparseSim::default();
53 test_expression_with_lib_and_profile_and_sim(expr, lib, profile, &mut sim, expected)
54}
55
56pub fn test_expression_with_lib_and_profile_and_sim(
57 expr: &str,
58 lib: &str,
59 profile: Profile,
60 sim: &mut impl Backend<ResultType = impl Into<Result>>,
61 expected: &Value,
62) -> String {
63 let mut stdout = vec![];
64 let mut out = GenericReceiver::new(&mut stdout);
65
66 let sources = SourceMap::new([("test".into(), lib.into())], Some(expr.into()));
67
68 let (std_id, store) = qsc::compile::package_store_with_stdlib(profile.into());
69
70 let mut interpreter = Interpreter::new(
71 sources,
72 PackageType::Exe,
73 profile.into(),
74 LanguageFeatures::default(),
75 store,
76 &[(std_id, None)],
77 )
78 .expect("test should compile");
79
80 let result = interpreter
81 .eval_entry_with_sim(sim, &mut out)
82 .expect("test should run successfully");
83
84 match (&expected, result) {
85 (&Value::Tuple(tup1, _), Value::Tuple(tup2, _)) if tup1.len() == tup2.len() => {
86 // If both values are tuples of the same length, we crack them open and compare elements
87 for (value1, value2) in tup1.iter().zip(tup2.iter()) {
88 if let (Value::Double(double1), Value::Double(double2)) = (value1, value2) {
89 // If both elements are doubles, we use approximate comparison
90 assert_doubles_almost_equal(*double1, *double2);
91 } else {
92 assert_eq!(value1, value2);
93 }
94 }
95 }
96 (&Value::Double(double1), Value::Double(double2)) => {
97 assert_doubles_almost_equal(*double1, double2);
98 }
99 (&expected, result) => assert_eq!(expected, &result),
100 }
101
102 String::from_utf8(stdout).expect("stdout should be valid utf8")
103}
104
105pub fn test_expression_fails_with_lib_and_profile_and_sim(
106 expr: &str,
107 lib: &str,
108 profile: Profile,
109 sim: &mut impl Backend<ResultType = impl Into<Result>>,
110) -> String {
111 let mut stdout = vec![];
112 let mut out = GenericReceiver::new(&mut stdout);
113
114 let sources = SourceMap::new([("test".into(), lib.into())], Some(expr.into()));
115
116 let (std_id, store) = qsc::compile::package_store_with_stdlib(profile.into());
117
118 let mut interpreter = Interpreter::new(
119 sources,
120 PackageType::Exe,
121 profile.into(),
122 LanguageFeatures::default(),
123 store,
124 &[(std_id, None)],
125 )
126 .expect("test should compile");
127
128 let result = interpreter
129 .eval_entry_with_sim(sim, &mut out)
130 .expect_err("test should run successfully");
131
132 assert!(
133 result.len() == 1,
134 "Expected a single error, got {:?}",
135 result.len()
136 );
137 let interpret::Error::Eval(err) = &result[0] else {
138 panic!("Expected an Eval error, got {:?}", result[0]);
139 };
140
141 err.error().error().to_string()
142}
143
144/// # Panics
145///
146/// Will panic if f64 values are significantly different.
147fn assert_doubles_almost_equal(val1: f64, val2: f64) {
148 let val1_abs = val1.abs();
149 let val2_abs = val2.abs();
150 if val1_abs < f64::MIN_POSITIVE && val2_abs < f64::MIN_POSITIVE {
151 // Note, that f64::MIN_POSITIVE is not the smallest representable positive number.
152 return;
153 }
154 assert!(
155 ((val1 - val2).abs() / (val1_abs + val2_abs)) < 1e-15,
156 "Significant difference between expected and actual values: val1={val1}, val2={val2}."
157 );
158}
159
160//
161// Core namespace
162//
163#[test]
164fn check_repeated() {
165 test_expression("Repeated(Zero, 0)", &Value::Array(vec![].into()));
166 test_expression(
167 "Repeated(One, 1)",
168 &Value::Array(vec![Value::RESULT_ONE].into()),
169 );
170 test_expression(
171 "Repeated(1, 2)",
172 &Value::Array(vec![Value::Int(1), Value::Int(1)].into()),
173 );
174 test_expression(
175 "Repeated(true, 3)",
176 &Value::Array(vec![Value::Bool(true), Value::Bool(true), Value::Bool(true)].into()),
177 );
178}
179
180#[test]
181fn check_exp_with_cnot() {
182 // This decomposition only holds if the magnitude of the angle used in Exp is correct and if the
183 // sign convention between Rx, Rz, and Exp is consistent.
184 test_expression(
185 indoc! {r#"{
186 import Std.Diagnostics.*;
187 import Std.Math.*;
188
189 use (aux, control, target) = (Qubit(), Qubit(), Qubit());
190 within {
191 H(aux);
192 CNOT(aux, control);
193 CNOT(aux, target);
194 }
195 apply {
196 let theta = PI() / 4.0;
197 Rx(-2.0 * theta, target);
198 Rz(-2.0 * theta, control);
199 Adjoint Exp([PauliZ, PauliX], theta, [control, target]);
200
201 Adjoint CNOT(control, target);
202 }
203
204 CheckAllZero([aux, control, target])
205 }"#},
206 &Value::Bool(true),
207 );
208}
209
210#[test]
211fn check_exp_with_swap() {
212 // This decomposition only holds if the magnitude of the angle used in Exp is correct.
213 test_expression(
214 indoc! {r#"{
215 import Std.Diagnostics.*;
216 import Std.Math.*;
217
218 use (aux, qs) = (Qubit(), Qubit[2]);
219 within {
220 H(aux);
221 CNOT(aux, qs[0]);
222 CNOT(aux, qs[1]);
223 }
224 apply {
225 let theta = PI() / 4.0;
226 Exp([PauliX, PauliX], theta, qs);
227 Exp([PauliY, PauliY], theta, qs);
228 Exp([PauliZ, PauliZ], theta, qs);
229
230 Adjoint SWAP(qs[0], qs[1]);
231 }
232
233 CheckAllZero([aux] + qs)
234 }"#},
235 &Value::Bool(true),
236 );
237}
238
239#[test]
240fn check_base_profile_measure_resets_aux_qubits() {
241 test_expression_with_lib_and_profile(
242 indoc! {"{
243 use q = Qubit();
244 X(q);
245 let result = M(q);
246 Reset(q);
247 result
248 }"},
249 "",
250 Profile::Base,
251 &Value::RESULT_ONE,
252 );
253}
254
255// just tests a single case of the stdlib reexports for the modern api,
256// to ensure that reexporting functionality doesn't break
257#[test]
258fn stdlib_reexport_single_case() {
259 test_expression(
260 r#" {
261 import Std.Arrays.Count;
262 }"#,
263 &Value::Tuple(vec![].into(), None),
264 );
265}
266