microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
minestarks-patch-1

Branches

Tags

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

Clone

HTTPS

Download ZIP

compiler/qsc_eval/src/intrinsic.rs

408lines · 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__t__body" => one_qubit_gate(|q| sim.t(q), arg, arg_span),
187 "__quantum__qis__t__adj" => one_qubit_gate(|q| sim.tadj(q), arg, arg_span),
188 "__quantum__qis__x__body" => one_qubit_gate(|q| sim.x(q), arg, arg_span),
189 "__quantum__qis__y__body" => one_qubit_gate(|q| sim.y(q), arg, arg_span),
190 "__quantum__qis__z__body" => one_qubit_gate(|q| sim.z(q), arg, arg_span),
191 "__quantum__qis__swap__body" => two_qubit_gate(|q0, q1| sim.swap(q0, q1), arg, arg_span),
192 "__quantum__qis__reset__body" => one_qubit_gate(|q| sim.reset(q), arg, arg_span),
193 "__quantum__qis__m__body" => Ok(Value::Result(
194 sim.m(arg
195 .unwrap_qubit()
196 .try_deref()
197 .ok_or(Error::QubitUsedAfterRelease(arg_span))?
198 .0)
199 .into(),
200 )),
201 "__quantum__qis__mresetz__body" => Ok(Value::Result(
202 sim.mresetz(
203 arg.unwrap_qubit()
204 .try_deref()
205 .ok_or(Error::QubitUsedAfterRelease(arg_span))?
206 .0,
207 )
208 .into(),
209 )),
210 _ => {
211 let qubits = arg.qubits();
212 let qubits_len = qubits.len();
213 let qubits = qubits
214 .iter()
215 .filter_map(|q| q.try_deref().map(|q| q.0))
216 .collect::<Vec<_>>();
217 if qubits.len() != qubits_len {
218 return Err(Error::QubitUsedAfterRelease(arg_span));
219 }
220 if let Some(result) = sim.custom_intrinsic(name, arg) {
221 match result {
222 Ok(value) => Ok(value),
223 Err(message) => Err(Error::IntrinsicFail(name.to_string(), message, name_span)),
224 }
225 } else {
226 Err(Error::UnknownIntrinsic(name.to_string(), name_span))
227 }
228 }
229 }
230}
231
232fn one_qubit_gate(
233 mut gate: impl FnMut(usize),
234 arg: Value,
235 arg_span: PackageSpan,
236) -> Result<Value, Error> {
237 gate(
238 arg.unwrap_qubit()
239 .try_deref()
240 .ok_or(Error::QubitUsedAfterRelease(arg_span))?
241 .0,
242 );
243 Ok(Value::unit())
244}
245
246fn two_qubit_gate(
247 mut gate: impl FnMut(usize, usize),
248 arg: Value,
249 arg_span: PackageSpan,
250) -> Result<Value, Error> {
251 let [x, y] = unwrap_tuple(arg);
252 if x == y {
253 Err(Error::QubitUniqueness(arg_span))
254 } else {
255 gate(
256 x.unwrap_qubit()
257 .try_deref()
258 .ok_or(Error::QubitUsedAfterRelease(arg_span))?
259 .0,
260 y.unwrap_qubit()
261 .try_deref()
262 .ok_or(Error::QubitUsedAfterRelease(arg_span))?
263 .0,
264 );
265 Ok(Value::unit())
266 }
267}
268
269fn one_qubit_rotation(
270 mut gate: impl FnMut(f64, usize),
271 arg: Value,
272 arg_span: PackageSpan,
273) -> Result<Value, Error> {
274 let [x, y] = unwrap_tuple(arg);
275 let angle = x.unwrap_double();
276 if angle.is_nan() || angle.is_infinite() {
277 Err(Error::InvalidRotationAngle(angle, arg_span))
278 } else {
279 gate(
280 angle,
281 y.unwrap_qubit()
282 .try_deref()
283 .ok_or(Error::QubitUsedAfterRelease(arg_span))?
284 .0,
285 );
286 Ok(Value::unit())
287 }
288}
289
290fn three_qubit_gate(
291 mut gate: impl FnMut(usize, usize, usize),
292 arg: Value,
293 arg_span: PackageSpan,
294) -> Result<Value, Error> {
295 let [x, y, z] = unwrap_tuple(arg);
296 if x == y || y == z || x == z {
297 Err(Error::QubitUniqueness(arg_span))
298 } else {
299 gate(
300 x.unwrap_qubit()
301 .try_deref()
302 .ok_or(Error::QubitUsedAfterRelease(arg_span))?
303 .0,
304 y.unwrap_qubit()
305 .try_deref()
306 .ok_or(Error::QubitUsedAfterRelease(arg_span))?
307 .0,
308 z.unwrap_qubit()
309 .try_deref()
310 .ok_or(Error::QubitUsedAfterRelease(arg_span))?
311 .0,
312 );
313 Ok(Value::unit())
314 }
315}
316
317fn two_qubit_rotation(
318 mut gate: impl FnMut(f64, usize, usize),
319 arg: Value,
320 arg_span: PackageSpan,
321) -> Result<Value, Error> {
322 let [x, y, z] = unwrap_tuple(arg);
323 let angle = x.unwrap_double();
324 if y == z {
325 Err(Error::QubitUniqueness(arg_span))
326 } else if angle.is_nan() || angle.is_infinite() {
327 Err(Error::InvalidRotationAngle(angle, arg_span))
328 } else {
329 gate(
330 angle,
331 y.unwrap_qubit()
332 .try_deref()
333 .ok_or(Error::QubitUsedAfterRelease(arg_span))?
334 .0,
335 z.unwrap_qubit()
336 .try_deref()
337 .ok_or(Error::QubitUsedAfterRelease(arg_span))?
338 .0,
339 );
340 Ok(Value::unit())
341 }
342}
343
344/// Performs relabeling of qubits from the a given left array to the corresponding right array.
345/// The function will swap qubits with the given function to match the new relabeling, returning an error
346/// if the qubits are not unique or if the relabeling is not a valid permutation.
347pub fn qubit_relabel(
348 arg: Value,
349 arg_span: PackageSpan,
350 mut swap: impl FnMut(usize, usize),
351) -> Result<Value, Error> {
352 let [left, right] = unwrap_tuple(arg);
353 let left = left.unwrap_array();
354 let left_len = left.len();
355 let left = left
356 .iter()
357 .filter_map(|q| q.clone().unwrap_qubit().try_deref().map(|q| q.0))
358 .collect::<Vec<_>>();
359 if left.len() != left_len {
360 return Err(Error::QubitUsedAfterRelease(arg_span));
361 }
362 let right = right.unwrap_array();
363 let right_len = right.len();
364 let right = right
365 .iter()
366 .filter_map(|q| q.clone().unwrap_qubit().try_deref().map(|q| q.0))
367 .collect::<Vec<_>>();
368 if right.len() != right_len {
369 return Err(Error::QubitUsedAfterRelease(arg_span));
370 }
371 let left_set = left.iter().collect::<FxHashSet<_>>();
372 let right_set = right.iter().collect::<FxHashSet<_>>();
373 if left.len() != left_set.len() || right.len() != right_set.len() {
374 return Err(Error::QubitUniqueness(arg_span));
375 }
376 if left_set != right_set {
377 return Err(Error::RelabelingMismatch(arg_span));
378 }
379
380 // Start with a mapping of each qubit to itself.
381 let mut mappings: FxHashMap<usize, usize> =
382 left.iter().copied().zip(left.iter().copied()).collect();
383 for (l, r) in left.into_iter().zip(right.into_iter()) {
384 // Trivial case where the qubit is already mapped to itself in the relabel, which can be short circuited.
385 if l == r {
386 continue;
387 }
388
389 // Check what each label currently maps to.
390 let mapped_l = *mappings.get(&l).expect("mapped qubit should be present");
391 let mapped_r = *mappings.get(&r).expect("mapped qubit should be present");
392
393 // We only need to swap if the label is not pointing to the correct qubit.
394 if mapped_l != r && mapped_r != l {
395 // Do a reverse lookup to find which label is currently mapped to the desired right qubit.
396 // This tells us which label to use in the swap, which we will use in the update of the mappings too.
397 let label_r = *mappings
398 .keys()
399 .find(|k| mappings[*k] == r)
400 .expect("mapped qubit should be present as both key and value");
401 swap(l, label_r);
402 mappings.insert(label_r, mapped_l);
403 mappings.insert(l, mapped_r);
404 }
405 }
406
407 Ok(Value::unit())
408}
409