microsoft/qdk
Publicmirrored from https://github.com/microsoft/qdkAvailable
compiler/qsc_eval/src/intrinsic.rs
234lines · modecode
| 1 | // Copyright (c) Microsoft Corporation. |
| 2 | // Licensed under the MIT License. |
| 3 | |
| 4 | mod utils; |
| 5 | |
| 6 | #[cfg(test)] |
| 7 | mod tests; |
| 8 | |
| 9 | use crate::{ |
| 10 | backend::Backend, |
| 11 | error::PackageSpan, |
| 12 | output::Receiver, |
| 13 | val::{self, Qubit, Value}, |
| 14 | Error, |
| 15 | }; |
| 16 | use num_bigint::BigInt; |
| 17 | use rand::{rngs::StdRng, Rng}; |
| 18 | use rustc_hash::FxHashSet; |
| 19 | use std::array; |
| 20 | |
| 21 | #[allow(clippy::too_many_lines)] |
| 22 | pub(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 | "DumpMachine" => { |
| 40 | let (state, qubit_count) = sim.capture_quantum_state(); |
| 41 | match out.state(state, qubit_count) { |
| 42 | Ok(()) => Ok(Value::unit()), |
| 43 | Err(_) => Err(Error::OutputFail(name_span)), |
| 44 | } |
| 45 | } |
| 46 | "DumpRegister" => { |
| 47 | let qubits = arg.unwrap_array(); |
| 48 | let qubits = qubits |
| 49 | .iter() |
| 50 | .map(|q| q.clone().unwrap_qubit().0) |
| 51 | .collect::<Vec<_>>(); |
| 52 | if qubits.len() != qubits.iter().collect::<FxHashSet<_>>().len() { |
| 53 | return Err(Error::QubitUniqueness(arg_span)); |
| 54 | } |
| 55 | let (state, qubit_count) = sim.capture_quantum_state(); |
| 56 | let state = utils::split_state(&qubits, state, qubit_count) |
| 57 | .map_err(|()| Error::QubitsNotSeparable(arg_span))?; |
| 58 | match out.state(state, qubits.len()) { |
| 59 | Ok(()) => Ok(Value::unit()), |
| 60 | Err(_) => Err(Error::OutputFail(name_span)), |
| 61 | } |
| 62 | } |
| 63 | "Message" => match out.message(&arg.unwrap_string()) { |
| 64 | Ok(()) => Ok(Value::unit()), |
| 65 | Err(_) => Err(Error::OutputFail(name_span)), |
| 66 | }, |
| 67 | "CheckZero" => Ok(Value::Bool(sim.qubit_is_zero(arg.unwrap_qubit().0))), |
| 68 | "ArcCos" => Ok(Value::Double(arg.unwrap_double().acos())), |
| 69 | "ArcSin" => Ok(Value::Double(arg.unwrap_double().asin())), |
| 70 | "ArcTan" => Ok(Value::Double(arg.unwrap_double().atan())), |
| 71 | "ArcTan2" => { |
| 72 | let [x, y] = unwrap_tuple(arg); |
| 73 | Ok(Value::Double(x.unwrap_double().atan2(y.unwrap_double()))) |
| 74 | } |
| 75 | "Cos" => Ok(Value::Double(arg.unwrap_double().cos())), |
| 76 | "Cosh" => Ok(Value::Double(arg.unwrap_double().cosh())), |
| 77 | "Sin" => Ok(Value::Double(arg.unwrap_double().sin())), |
| 78 | "Sinh" => Ok(Value::Double(arg.unwrap_double().sinh())), |
| 79 | "Tan" => Ok(Value::Double(arg.unwrap_double().tan())), |
| 80 | "Tanh" => Ok(Value::Double(arg.unwrap_double().tanh())), |
| 81 | "Sqrt" => Ok(Value::Double(arg.unwrap_double().sqrt())), |
| 82 | "Log" => Ok(Value::Double(arg.unwrap_double().ln())), |
| 83 | "DrawRandomInt" => { |
| 84 | let [lo, hi] = unwrap_tuple(arg); |
| 85 | let lo = lo.unwrap_int(); |
| 86 | let hi = hi.unwrap_int(); |
| 87 | if lo > hi { |
| 88 | Err(Error::EmptyRange(arg_span)) |
| 89 | } else { |
| 90 | Ok(Value::Int(rng.gen_range(lo..=hi))) |
| 91 | } |
| 92 | } |
| 93 | "DrawRandomDouble" => { |
| 94 | let [lo, hi] = unwrap_tuple(arg); |
| 95 | let lo = lo.unwrap_double(); |
| 96 | let hi = hi.unwrap_double(); |
| 97 | if lo > hi { |
| 98 | Err(Error::EmptyRange(arg_span)) |
| 99 | } else { |
| 100 | Ok(Value::Double(rng.gen_range(lo..=hi))) |
| 101 | } |
| 102 | } |
| 103 | #[allow(clippy::cast_possible_truncation)] |
| 104 | "Truncate" => Ok(Value::Int(arg.unwrap_double() as i64)), |
| 105 | "__quantum__rt__qubit_allocate" => Ok(Value::Qubit(Qubit(sim.qubit_allocate()))), |
| 106 | "__quantum__rt__qubit_release" => { |
| 107 | let qubit = arg.unwrap_qubit().0; |
| 108 | if sim.qubit_is_zero(qubit) { |
| 109 | sim.qubit_release(qubit); |
| 110 | Ok(Value::unit()) |
| 111 | } else { |
| 112 | Err(Error::ReleasedQubitNotZero(qubit, arg_span)) |
| 113 | } |
| 114 | } |
| 115 | "__quantum__qis__ccx__body" => { |
| 116 | three_qubit_gate(|ctl0, ctl1, q| sim.ccx(ctl0, ctl1, q), arg, arg_span) |
| 117 | } |
| 118 | "__quantum__qis__cx__body" => two_qubit_gate(|ctl, q| sim.cx(ctl, q), arg, arg_span), |
| 119 | "__quantum__qis__cy__body" => two_qubit_gate(|ctl, q| sim.cy(ctl, q), arg, arg_span), |
| 120 | "__quantum__qis__cz__body" => two_qubit_gate(|ctl, q| sim.cz(ctl, q), arg, arg_span), |
| 121 | "__quantum__qis__rx__body" => { |
| 122 | one_qubit_rotation(|theta, q| sim.rx(theta, q), arg, arg_span) |
| 123 | } |
| 124 | "__quantum__qis__rxx__body" => { |
| 125 | two_qubit_rotation(|theta, q0, q1| sim.rxx(theta, q0, q1), arg, arg_span) |
| 126 | } |
| 127 | "__quantum__qis__ry__body" => { |
| 128 | one_qubit_rotation(|theta, q| sim.ry(theta, q), arg, arg_span) |
| 129 | } |
| 130 | "__quantum__qis__ryy__body" => { |
| 131 | two_qubit_rotation(|theta, q0, q1| sim.ryy(theta, q0, q1), arg, arg_span) |
| 132 | } |
| 133 | "__quantum__qis__rz__body" => { |
| 134 | one_qubit_rotation(|theta, q| sim.rz(theta, q), arg, arg_span) |
| 135 | } |
| 136 | "__quantum__qis__rzz__body" => { |
| 137 | two_qubit_rotation(|theta, q0, q1| sim.rzz(theta, q0, q1), arg, arg_span) |
| 138 | } |
| 139 | "__quantum__qis__h__body" => Ok(one_qubit_gate(|q| sim.h(q), arg)), |
| 140 | "__quantum__qis__s__body" => Ok(one_qubit_gate(|q| sim.s(q), arg)), |
| 141 | "__quantum__qis__s__adj" => Ok(one_qubit_gate(|q| sim.sadj(q), arg)), |
| 142 | "__quantum__qis__t__body" => Ok(one_qubit_gate(|q| sim.t(q), arg)), |
| 143 | "__quantum__qis__t__adj" => Ok(one_qubit_gate(|q| sim.tadj(q), arg)), |
| 144 | "__quantum__qis__x__body" => Ok(one_qubit_gate(|q| sim.x(q), arg)), |
| 145 | "__quantum__qis__y__body" => Ok(one_qubit_gate(|q| sim.y(q), arg)), |
| 146 | "__quantum__qis__z__body" => Ok(one_qubit_gate(|q| sim.z(q), arg)), |
| 147 | "__quantum__qis__swap__body" => two_qubit_gate(|q0, q1| sim.swap(q0, q1), arg, arg_span), |
| 148 | "__quantum__qis__reset__body" => Ok(one_qubit_gate(|q| sim.reset(q), arg)), |
| 149 | "__quantum__qis__m__body" => Ok(Value::Result(sim.m(arg.unwrap_qubit().0).into())), |
| 150 | "__quantum__qis__mresetz__body" => { |
| 151 | Ok(Value::Result(sim.mresetz(arg.unwrap_qubit().0).into())) |
| 152 | } |
| 153 | _ => { |
| 154 | if let Some(result) = sim.custom_intrinsic(name, arg) { |
| 155 | match result { |
| 156 | Ok(value) => Ok(value), |
| 157 | Err(message) => Err(Error::IntrinsicFail(name.to_string(), message, name_span)), |
| 158 | } |
| 159 | } else { |
| 160 | Err(Error::UnknownIntrinsic(name.to_string(), name_span)) |
| 161 | } |
| 162 | } |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | fn one_qubit_gate(mut gate: impl FnMut(usize), arg: Value) -> Value { |
| 167 | gate(arg.unwrap_qubit().0); |
| 168 | Value::unit() |
| 169 | } |
| 170 | |
| 171 | fn two_qubit_gate( |
| 172 | mut gate: impl FnMut(usize, usize), |
| 173 | arg: Value, |
| 174 | arg_span: PackageSpan, |
| 175 | ) -> Result<Value, Error> { |
| 176 | let [x, y] = unwrap_tuple(arg); |
| 177 | if x == y { |
| 178 | Err(Error::QubitUniqueness(arg_span)) |
| 179 | } else { |
| 180 | gate(x.unwrap_qubit().0, y.unwrap_qubit().0); |
| 181 | Ok(Value::unit()) |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | fn one_qubit_rotation( |
| 186 | mut gate: impl FnMut(f64, usize), |
| 187 | arg: Value, |
| 188 | arg_span: PackageSpan, |
| 189 | ) -> Result<Value, Error> { |
| 190 | let [x, y] = unwrap_tuple(arg); |
| 191 | let angle = x.unwrap_double(); |
| 192 | if angle.is_nan() || angle.is_infinite() { |
| 193 | Err(Error::InvalidRotationAngle(angle, arg_span)) |
| 194 | } else { |
| 195 | gate(angle, y.unwrap_qubit().0); |
| 196 | Ok(Value::unit()) |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | fn three_qubit_gate( |
| 201 | mut gate: impl FnMut(usize, usize, usize), |
| 202 | arg: Value, |
| 203 | arg_span: PackageSpan, |
| 204 | ) -> Result<Value, Error> { |
| 205 | let [x, y, z] = unwrap_tuple(arg); |
| 206 | if x == y || y == z || x == z { |
| 207 | Err(Error::QubitUniqueness(arg_span)) |
| 208 | } else { |
| 209 | gate(x.unwrap_qubit().0, y.unwrap_qubit().0, z.unwrap_qubit().0); |
| 210 | Ok(Value::unit()) |
| 211 | } |
| 212 | } |
| 213 | |
| 214 | fn two_qubit_rotation( |
| 215 | mut gate: impl FnMut(f64, usize, usize), |
| 216 | arg: Value, |
| 217 | arg_span: PackageSpan, |
| 218 | ) -> Result<Value, Error> { |
| 219 | let [x, y, z] = unwrap_tuple(arg); |
| 220 | let angle = x.unwrap_double(); |
| 221 | if y == z { |
| 222 | Err(Error::QubitUniqueness(arg_span)) |
| 223 | } else if angle.is_nan() || angle.is_infinite() { |
| 224 | Err(Error::InvalidRotationAngle(angle, arg_span)) |
| 225 | } else { |
| 226 | gate(angle, y.unwrap_qubit().0, z.unwrap_qubit().0); |
| 227 | Ok(Value::unit()) |
| 228 | } |
| 229 | } |
| 230 | |
| 231 | fn unwrap_tuple<const N: usize>(value: Value) -> [Value; N] { |
| 232 | let values = value.unwrap_tuple(); |
| 233 | array::from_fn(|i| values[i].clone()) |
| 234 | } |