microsoft/qdk

Public

mirrored from https://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.18.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

source/compiler/qsc_eval/src/intrinsic.rs

409lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4mod utils;
5
6#[cfg(test)]
7mod tests;
8
9use crate::{
10 backend::Backend,
11 error::PackageSpan,
12 output::Receiver,
13 val::{self, unwrap_tuple, Value},
14 Error, Rc,
15};
16use num_bigint::BigInt;
17use rand::{rngs::StdRng, Rng};
18use rustc_hash::{FxHashMap, FxHashSet};
19use std::convert::TryFrom;
20
21#[allow(clippy::too_many_lines)]
22pub(crate) fn call(
23 name: &str,
24 name_span: PackageSpan,
25 arg: Value,
26 arg_span: PackageSpan,
27 sim: &mut dyn Backend<ResultType = impl Into<val::Result>>,
28 rng: &mut StdRng,
29 out: &mut dyn Receiver,
30) -> Result<Value, Error> {
31 match name {
32 "Length" => match arg.unwrap_array().len().try_into() {
33 Ok(len) => Ok(Value::Int(len)),
34 Err(_) => Err(Error::ArrayTooLarge(arg_span)),
35 },
36 #[allow(clippy::cast_precision_loss)]
37 "IntAsDouble" => Ok(Value::Double(arg.unwrap_int() as f64)),
38 "IntAsBigInt" => Ok(Value::BigInt(BigInt::from(arg.unwrap_int()))),
39 "DoubleAsStringWithPrecision" => {
40 let [input, prec_val] = unwrap_tuple(arg);
41 let prec_int = prec_val.unwrap_int();
42 if prec_int < 0 {
43 Err(Error::InvalidNegativeInt(prec_int, arg_span))
44 } else {
45 let precision = usize::try_from(prec_int).expect("integer value");
46 let is_zero = if precision == 0 { "." } else { "" };
47 Ok(Value::String(Rc::from(format!(
48 "{:.*}{}",
49 precision,
50 input.unwrap_double(),
51 is_zero
52 ))))
53 }
54 }
55 "DumpMachine" => {
56 let (state, qubit_count) = sim.capture_quantum_state();
57 match out.state(state, qubit_count) {
58 Ok(()) => Ok(Value::unit()),
59 Err(_) => Err(Error::OutputFail(name_span)),
60 }
61 }
62 "DumpRegister" => {
63 let qubits = arg.unwrap_array();
64 let qubits_len = qubits.len();
65 let qubits = qubits
66 .iter()
67 .filter_map(|q| q.clone().unwrap_qubit().try_deref().map(|q| q.0))
68 .collect::<Vec<_>>();
69 if qubits.len() != qubits_len {
70 return Err(Error::QubitUsedAfterRelease(arg_span));
71 }
72 if qubits.len() != qubits.iter().collect::<FxHashSet<_>>().len() {
73 return Err(Error::QubitUniqueness(arg_span));
74 }
75 let (state, qubit_count) = sim.capture_quantum_state();
76 let state = utils::split_state(&qubits, &state, qubit_count)
77 .map_err(|()| Error::QubitsNotSeparable(arg_span))?;
78 match out.state(state, qubits.len()) {
79 Ok(()) => Ok(Value::unit()),
80 Err(_) => Err(Error::OutputFail(name_span)),
81 }
82 }
83 "DumpMatrix" => {
84 let qubits = arg.unwrap_array();
85 let qubits_len = qubits.len();
86 let qubits = qubits
87 .iter()
88 .filter_map(|q| q.clone().unwrap_qubit().try_deref().map(|q| q.0))
89 .collect::<Vec<_>>();
90 if qubits.len() != qubits_len {
91 return Err(Error::QubitUsedAfterRelease(arg_span));
92 }
93 if qubits.len() != qubits.iter().collect::<FxHashSet<_>>().len() {
94 return Err(Error::QubitUniqueness(arg_span));
95 }
96 let (state, qubit_count) = sim.capture_quantum_state();
97 let state = utils::split_state(&qubits, &state, qubit_count)
98 .map_err(|()| Error::QubitsNotSeparable(arg_span))?;
99 let matrix = utils::state_to_matrix(state, qubits.len() / 2);
100 match out.matrix(matrix) {
101 Ok(()) => Ok(Value::unit()),
102 Err(_) => Err(Error::OutputFail(name_span)),
103 }
104 }
105 "PermuteLabels" => qubit_relabel(arg, arg_span, |q0, q1| sim.qubit_swap_id(q0, q1)),
106 "Message" => match out.message(&arg.unwrap_string()) {
107 Ok(()) => Ok(Value::unit()),
108 Err(_) => Err(Error::OutputFail(name_span)),
109 },
110 "CheckZero" => Ok(Value::Bool(
111 sim.qubit_is_zero(
112 arg.unwrap_qubit()
113 .try_deref()
114 .ok_or(Error::QubitUsedAfterRelease(arg_span))?
115 .0,
116 ),
117 )),
118 "ArcCos" => Ok(Value::Double(arg.unwrap_double().acos())),
119 "ArcSin" => Ok(Value::Double(arg.unwrap_double().asin())),
120 "ArcTan" => Ok(Value::Double(arg.unwrap_double().atan())),
121 "ArcTan2" => {
122 let [x, y] = unwrap_tuple(arg);
123 Ok(Value::Double(x.unwrap_double().atan2(y.unwrap_double())))
124 }
125 "Cos" => Ok(Value::Double(arg.unwrap_double().cos())),
126 "Cosh" => Ok(Value::Double(arg.unwrap_double().cosh())),
127 "Sin" => Ok(Value::Double(arg.unwrap_double().sin())),
128 "Sinh" => Ok(Value::Double(arg.unwrap_double().sinh())),
129 "Tan" => Ok(Value::Double(arg.unwrap_double().tan())),
130 "Tanh" => Ok(Value::Double(arg.unwrap_double().tanh())),
131 "Sqrt" => Ok(Value::Double(arg.unwrap_double().sqrt())),
132 "Log" => Ok(Value::Double(arg.unwrap_double().ln())),
133 "DrawRandomInt" => {
134 let [lo, hi] = unwrap_tuple(arg);
135 let lo = lo.unwrap_int();
136 let hi = hi.unwrap_int();
137 if lo > hi {
138 Err(Error::EmptyRange(arg_span))
139 } else {
140 Ok(Value::Int(rng.gen_range(lo..=hi)))
141 }
142 }
143 "DrawRandomDouble" => {
144 let [lo, hi] = unwrap_tuple(arg);
145 let lo = lo.unwrap_double();
146 let hi = hi.unwrap_double();
147 if lo > hi {
148 Err(Error::EmptyRange(arg_span))
149 } else {
150 Ok(Value::Double(rng.gen_range(lo..=hi)))
151 }
152 }
153 "DrawRandomBool" => {
154 let p = arg.unwrap_double();
155 Ok(Value::Bool(rng.gen_bool(p)))
156 }
157 #[allow(clippy::cast_possible_truncation)]
158 "Truncate" => Ok(Value::Int(arg.unwrap_double() as i64)),
159 "__quantum__qis__ccx__body" => {
160 three_qubit_gate(|ctl0, ctl1, q| sim.ccx(ctl0, ctl1, q), arg, arg_span)
161 }
162 "__quantum__qis__cx__body" => two_qubit_gate(|ctl, q| sim.cx(ctl, q), arg, arg_span),
163 "__quantum__qis__cy__body" => two_qubit_gate(|ctl, q| sim.cy(ctl, q), arg, arg_span),
164 "__quantum__qis__cz__body" => two_qubit_gate(|ctl, q| sim.cz(ctl, q), arg, arg_span),
165 "__quantum__qis__rx__body" => {
166 one_qubit_rotation(|theta, q| sim.rx(theta, q), arg, arg_span)
167 }
168 "__quantum__qis__rxx__body" => {
169 two_qubit_rotation(|theta, q0, q1| sim.rxx(theta, q0, q1), arg, arg_span)
170 }
171 "__quantum__qis__ry__body" => {
172 one_qubit_rotation(|theta, q| sim.ry(theta, q), arg, arg_span)
173 }
174 "__quantum__qis__ryy__body" => {
175 two_qubit_rotation(|theta, q0, q1| sim.ryy(theta, q0, q1), arg, arg_span)
176 }
177 "__quantum__qis__rz__body" => {
178 one_qubit_rotation(|theta, q| sim.rz(theta, q), arg, arg_span)
179 }
180 "__quantum__qis__rzz__body" => {
181 two_qubit_rotation(|theta, q0, q1| sim.rzz(theta, q0, q1), arg, arg_span)
182 }
183 "__quantum__qis__h__body" => one_qubit_gate(|q| sim.h(q), arg, arg_span),
184 "__quantum__qis__s__body" => one_qubit_gate(|q| sim.s(q), arg, arg_span),
185 "__quantum__qis__s__adj" => one_qubit_gate(|q| sim.sadj(q), arg, arg_span),
186 "__quantum__qis__sx__body" => one_qubit_gate(|q| sim.sx(q), arg, arg_span),
187 "__quantum__qis__t__body" => one_qubit_gate(|q| sim.t(q), arg, arg_span),
188 "__quantum__qis__t__adj" => one_qubit_gate(|q| sim.tadj(q), arg, arg_span),
189 "__quantum__qis__x__body" => one_qubit_gate(|q| sim.x(q), arg, arg_span),
190 "__quantum__qis__y__body" => one_qubit_gate(|q| sim.y(q), arg, arg_span),
191 "__quantum__qis__z__body" => one_qubit_gate(|q| sim.z(q), arg, arg_span),
192 "__quantum__qis__swap__body" => two_qubit_gate(|q0, q1| sim.swap(q0, q1), arg, arg_span),
193 "__quantum__qis__reset__body" => one_qubit_gate(|q| sim.reset(q), arg, arg_span),
194 "__quantum__qis__m__body" => Ok(Value::Result(
195 sim.m(arg
196 .unwrap_qubit()
197 .try_deref()
198 .ok_or(Error::QubitUsedAfterRelease(arg_span))?
199 .0)
200 .into(),
201 )),
202 "__quantum__qis__mresetz__body" => Ok(Value::Result(
203 sim.mresetz(
204 arg.unwrap_qubit()
205 .try_deref()
206 .ok_or(Error::QubitUsedAfterRelease(arg_span))?
207 .0,
208 )
209 .into(),
210 )),
211 _ => {
212 let qubits = arg.qubits();
213 let qubits_len = qubits.len();
214 let qubits = qubits
215 .iter()
216 .filter_map(|q| q.try_deref().map(|q| q.0))
217 .collect::<Vec<_>>();
218 if qubits.len() != qubits_len {
219 return Err(Error::QubitUsedAfterRelease(arg_span));
220 }
221 if let Some(result) = sim.custom_intrinsic(name, arg) {
222 match result {
223 Ok(value) => Ok(value),
224 Err(message) => Err(Error::IntrinsicFail(name.to_string(), message, name_span)),
225 }
226 } else {
227 Err(Error::UnknownIntrinsic(name.to_string(), name_span))
228 }
229 }
230 }
231}
232
233fn one_qubit_gate(
234 mut gate: impl FnMut(usize),
235 arg: Value,
236 arg_span: PackageSpan,
237) -> Result<Value, Error> {
238 gate(
239 arg.unwrap_qubit()
240 .try_deref()
241 .ok_or(Error::QubitUsedAfterRelease(arg_span))?
242 .0,
243 );
244 Ok(Value::unit())
245}
246
247fn two_qubit_gate(
248 mut gate: impl FnMut(usize, usize),
249 arg: Value,
250 arg_span: PackageSpan,
251) -> Result<Value, Error> {
252 let [x, y] = unwrap_tuple(arg);
253 if x == y {
254 Err(Error::QubitUniqueness(arg_span))
255 } else {
256 gate(
257 x.unwrap_qubit()
258 .try_deref()
259 .ok_or(Error::QubitUsedAfterRelease(arg_span))?
260 .0,
261 y.unwrap_qubit()
262 .try_deref()
263 .ok_or(Error::QubitUsedAfterRelease(arg_span))?
264 .0,
265 );
266 Ok(Value::unit())
267 }
268}
269
270fn one_qubit_rotation(
271 mut gate: impl FnMut(f64, usize),
272 arg: Value,
273 arg_span: PackageSpan,
274) -> Result<Value, Error> {
275 let [x, y] = unwrap_tuple(arg);
276 let angle = x.unwrap_double();
277 if angle.is_nan() || angle.is_infinite() {
278 Err(Error::InvalidRotationAngle(angle, arg_span))
279 } else {
280 gate(
281 angle,
282 y.unwrap_qubit()
283 .try_deref()
284 .ok_or(Error::QubitUsedAfterRelease(arg_span))?
285 .0,
286 );
287 Ok(Value::unit())
288 }
289}
290
291fn three_qubit_gate(
292 mut gate: impl FnMut(usize, usize, usize),
293 arg: Value,
294 arg_span: PackageSpan,
295) -> Result<Value, Error> {
296 let [x, y, z] = unwrap_tuple(arg);
297 if x == y || y == z || x == z {
298 Err(Error::QubitUniqueness(arg_span))
299 } else {
300 gate(
301 x.unwrap_qubit()
302 .try_deref()
303 .ok_or(Error::QubitUsedAfterRelease(arg_span))?
304 .0,
305 y.unwrap_qubit()
306 .try_deref()
307 .ok_or(Error::QubitUsedAfterRelease(arg_span))?
308 .0,
309 z.unwrap_qubit()
310 .try_deref()
311 .ok_or(Error::QubitUsedAfterRelease(arg_span))?
312 .0,
313 );
314 Ok(Value::unit())
315 }
316}
317
318fn two_qubit_rotation(
319 mut gate: impl FnMut(f64, usize, usize),
320 arg: Value,
321 arg_span: PackageSpan,
322) -> Result<Value, Error> {
323 let [x, y, z] = unwrap_tuple(arg);
324 let angle = x.unwrap_double();
325 if y == z {
326 Err(Error::QubitUniqueness(arg_span))
327 } else if angle.is_nan() || angle.is_infinite() {
328 Err(Error::InvalidRotationAngle(angle, arg_span))
329 } else {
330 gate(
331 angle,
332 y.unwrap_qubit()
333 .try_deref()
334 .ok_or(Error::QubitUsedAfterRelease(arg_span))?
335 .0,
336 z.unwrap_qubit()
337 .try_deref()
338 .ok_or(Error::QubitUsedAfterRelease(arg_span))?
339 .0,
340 );
341 Ok(Value::unit())
342 }
343}
344
345/// Performs relabeling of qubits from the a given left array to the corresponding right array.
346/// The function will swap qubits with the given function to match the new relabeling, returning an error
347/// if the qubits are not unique or if the relabeling is not a valid permutation.
348pub fn qubit_relabel(
349 arg: Value,
350 arg_span: PackageSpan,
351 mut swap: impl FnMut(usize, usize),
352) -> Result<Value, Error> {
353 let [left, right] = unwrap_tuple(arg);
354 let left = left.unwrap_array();
355 let left_len = left.len();
356 let left = left
357 .iter()
358 .filter_map(|q| q.clone().unwrap_qubit().try_deref().map(|q| q.0))
359 .collect::<Vec<_>>();
360 if left.len() != left_len {
361 return Err(Error::QubitUsedAfterRelease(arg_span));
362 }
363 let right = right.unwrap_array();
364 let right_len = right.len();
365 let right = right
366 .iter()
367 .filter_map(|q| q.clone().unwrap_qubit().try_deref().map(|q| q.0))
368 .collect::<Vec<_>>();
369 if right.len() != right_len {
370 return Err(Error::QubitUsedAfterRelease(arg_span));
371 }
372 let left_set = left.iter().collect::<FxHashSet<_>>();
373 let right_set = right.iter().collect::<FxHashSet<_>>();
374 if left.len() != left_set.len() || right.len() != right_set.len() {
375 return Err(Error::QubitUniqueness(arg_span));
376 }
377 if left_set != right_set {
378 return Err(Error::RelabelingMismatch(arg_span));
379 }
380
381 // Start with a mapping of each qubit to itself.
382 let mut mappings: FxHashMap<usize, usize> =
383 left.iter().copied().zip(left.iter().copied()).collect();
384 for (l, r) in left.into_iter().zip(right.into_iter()) {
385 // Trivial case where the qubit is already mapped to itself in the relabel, which can be short circuited.
386 if l == r {
387 continue;
388 }
389
390 // Check what each label currently maps to.
391 let mapped_l = *mappings.get(&l).expect("mapped qubit should be present");
392 let mapped_r = *mappings.get(&r).expect("mapped qubit should be present");
393
394 // We only need to swap if the label is not pointing to the correct qubit.
395 if mapped_l != r && mapped_r != l {
396 // Do a reverse lookup to find which label is currently mapped to the desired right qubit.
397 // This tells us which label to use in the swap, which we will use in the update of the mappings too.
398 let label_r = *mappings
399 .keys()
400 .find(|k| mappings[*k] == r)
401 .expect("mapped qubit should be present as both key and value");
402 swap(l, label_r);
403 mappings.insert(label_r, mapped_l);
404 mappings.insert(l, mapped_r);
405 }
406 }
407
408 Ok(Value::unit())
409}
410