microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
513f6a768700f0bad2c5e68225186ad0f5b94728

Branches

Tags

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

Clone

HTTPS

Download ZIP

library/src/tests.rs

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