microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.9.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

compiler/qsc_eval/src/backend.rs

492lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use num_bigint::BigUint;
5use num_complex::Complex;
6use quantum_sparse_sim::QuantumSim;
7use rand::RngCore;
8
9use crate::val::Value;
10
11/// The trait that must be implemented by a quantum backend, whose functions will be invoked when
12/// quantum intrinsics are called.
13pub trait Backend {
14 type ResultType;
15
16 fn ccx(&mut self, _ctl0: usize, _ctl1: usize, _q: usize) {
17 unimplemented!("ccx gate");
18 }
19 fn cx(&mut self, _ctl: usize, _q: usize) {
20 unimplemented!("cx gate");
21 }
22 fn cy(&mut self, _ctl: usize, _q: usize) {
23 unimplemented!("cy gate");
24 }
25 fn cz(&mut self, _ctl: usize, _q: usize) {
26 unimplemented!("cz gate");
27 }
28 fn h(&mut self, _q: usize) {
29 unimplemented!("h gate");
30 }
31 fn m(&mut self, _q: usize) -> Self::ResultType {
32 unimplemented!("m operation");
33 }
34 fn mresetz(&mut self, _q: usize) -> Self::ResultType {
35 unimplemented!("mresetz operation");
36 }
37 fn reset(&mut self, _q: usize) {
38 unimplemented!("reset gate");
39 }
40 fn rx(&mut self, _theta: f64, _q: usize) {
41 unimplemented!("rx gate");
42 }
43 fn rxx(&mut self, _theta: f64, _q0: usize, _q1: usize) {
44 unimplemented!("rxx gate");
45 }
46 fn ry(&mut self, _theta: f64, _q: usize) {
47 unimplemented!("ry gate");
48 }
49 fn ryy(&mut self, _theta: f64, _q0: usize, _q1: usize) {
50 unimplemented!("ryy gate");
51 }
52 fn rz(&mut self, _theta: f64, _q: usize) {
53 unimplemented!("rz gate");
54 }
55 fn rzz(&mut self, _theta: f64, _q0: usize, _q1: usize) {
56 unimplemented!("rzz gate");
57 }
58 fn sadj(&mut self, _q: usize) {
59 unimplemented!("sadj gate");
60 }
61 fn s(&mut self, _q: usize) {
62 unimplemented!("s gate");
63 }
64 fn swap(&mut self, _q0: usize, _q1: usize) {
65 unimplemented!("swap gate");
66 }
67 fn tadj(&mut self, _q: usize) {
68 unimplemented!("tadj gate");
69 }
70 fn t(&mut self, _q: usize) {
71 unimplemented!("t gate");
72 }
73 fn x(&mut self, _q: usize) {
74 unimplemented!("x gate");
75 }
76 fn y(&mut self, _q: usize) {
77 unimplemented!("y gate");
78 }
79 fn z(&mut self, _q: usize) {
80 unimplemented!("z gate");
81 }
82 fn qubit_allocate(&mut self) -> usize {
83 unimplemented!("qubit_allocate operation");
84 }
85 fn qubit_release(&mut self, _q: usize) {
86 unimplemented!("qubit_release operation");
87 }
88 fn qubit_swap_id(&mut self, _q0: usize, _q1: usize) {
89 unimplemented!("qubit_swap_id operation");
90 }
91 fn capture_quantum_state(&mut self) -> (Vec<(BigUint, Complex<f64>)>, usize) {
92 unimplemented!("capture_quantum_state operation");
93 }
94 fn qubit_is_zero(&mut self, _q: usize) -> bool {
95 unimplemented!("qubit_is_zero operation");
96 }
97 fn custom_intrinsic(&mut self, _name: &str, _arg: Value) -> Option<Result<Value, String>> {
98 None
99 }
100 fn set_seed(&mut self, _seed: Option<u64>) {}
101}
102
103/// Default backend used when targeting sparse simulation.
104pub struct SparseSim {
105 pub sim: QuantumSim,
106}
107
108impl Default for SparseSim {
109 fn default() -> Self {
110 Self::new()
111 }
112}
113
114impl SparseSim {
115 #[must_use]
116 pub fn new() -> Self {
117 Self {
118 sim: QuantumSim::new(None),
119 }
120 }
121}
122
123impl Backend for SparseSim {
124 type ResultType = bool;
125
126 fn ccx(&mut self, ctl0: usize, ctl1: usize, q: usize) {
127 self.sim.mcx(&[ctl0, ctl1], q);
128 }
129
130 fn cx(&mut self, ctl: usize, q: usize) {
131 self.sim.mcx(&[ctl], q);
132 }
133
134 fn cy(&mut self, ctl: usize, q: usize) {
135 self.sim.mcy(&[ctl], q);
136 }
137
138 fn cz(&mut self, ctl: usize, q: usize) {
139 self.sim.mcz(&[ctl], q);
140 }
141
142 fn h(&mut self, q: usize) {
143 self.sim.h(q);
144 }
145
146 fn m(&mut self, q: usize) -> Self::ResultType {
147 self.sim.measure(q)
148 }
149
150 fn mresetz(&mut self, q: usize) -> Self::ResultType {
151 let res = self.sim.measure(q);
152 if res {
153 self.sim.x(q);
154 }
155 res
156 }
157
158 fn reset(&mut self, q: usize) {
159 self.mresetz(q);
160 }
161
162 fn rx(&mut self, theta: f64, q: usize) {
163 self.sim.rx(theta, q);
164 }
165
166 fn rxx(&mut self, theta: f64, q0: usize, q1: usize) {
167 self.h(q0);
168 self.h(q1);
169 self.rzz(theta, q0, q1);
170 self.h(q1);
171 self.h(q0);
172 }
173
174 fn ry(&mut self, theta: f64, q: usize) {
175 self.sim.ry(theta, q);
176 }
177
178 fn ryy(&mut self, theta: f64, q0: usize, q1: usize) {
179 self.h(q0);
180 self.s(q0);
181 self.h(q0);
182 self.h(q1);
183 self.s(q1);
184 self.h(q1);
185 self.rzz(theta, q0, q1);
186 self.h(q1);
187 self.sadj(q1);
188 self.h(q1);
189 self.h(q0);
190 self.sadj(q0);
191 self.h(q0);
192 }
193
194 fn rz(&mut self, theta: f64, q: usize) {
195 self.sim.rz(theta, q);
196 }
197
198 fn rzz(&mut self, theta: f64, q0: usize, q1: usize) {
199 self.cx(q1, q0);
200 self.rz(theta, q0);
201 self.cx(q1, q0);
202 }
203
204 fn sadj(&mut self, q: usize) {
205 self.sim.sadj(q);
206 }
207
208 fn s(&mut self, q: usize) {
209 self.sim.s(q);
210 }
211
212 fn swap(&mut self, q0: usize, q1: usize) {
213 self.sim.swap_qubit_ids(q0, q1);
214 }
215
216 fn tadj(&mut self, q: usize) {
217 self.sim.tadj(q);
218 }
219
220 fn t(&mut self, q: usize) {
221 self.sim.t(q);
222 }
223
224 fn x(&mut self, q: usize) {
225 self.sim.x(q);
226 }
227
228 fn y(&mut self, q: usize) {
229 self.sim.y(q);
230 }
231
232 fn z(&mut self, q: usize) {
233 self.sim.z(q);
234 }
235
236 fn qubit_allocate(&mut self) -> usize {
237 self.sim.allocate()
238 }
239
240 fn qubit_release(&mut self, q: usize) {
241 self.sim.release(q);
242 }
243
244 fn qubit_swap_id(&mut self, q0: usize, q1: usize) {
245 self.sim.swap_qubit_ids(q0, q1);
246 }
247
248 fn capture_quantum_state(&mut self) -> (Vec<(BigUint, Complex<f64>)>, usize) {
249 let (state, count) = self.sim.get_state();
250 // Because the simulator returns the state indices with opposite endianness from the
251 // expected one, we need to reverse the bit order of the indices.
252 let mut new_state = state
253 .into_iter()
254 .map(|(idx, val)| {
255 let mut new_idx = BigUint::default();
256 for i in 0..(count as u64) {
257 if idx.bit((count as u64) - 1 - i) {
258 new_idx.set_bit(i, true);
259 }
260 }
261 (new_idx, val)
262 })
263 .collect::<Vec<_>>();
264 new_state.sort_unstable_by(|a, b| a.0.cmp(&b.0));
265 (new_state, count)
266 }
267
268 fn qubit_is_zero(&mut self, q: usize) -> bool {
269 self.sim.qubit_is_zero(q)
270 }
271
272 fn custom_intrinsic(&mut self, name: &str, arg: Value) -> Option<Result<Value, String>> {
273 match name {
274 "GlobalPhase" => {
275 // Apply a global phase to the simulation by doing an Rz to a fresh qubit.
276 // The controls list may be empty, in which case the phase is applied unconditionally.
277 let [ctls_val, theta] = &*arg.unwrap_tuple() else {
278 panic!("tuple arity for GlobalPhase intrinsic should be 2");
279 };
280 let ctls = ctls_val
281 .clone()
282 .unwrap_array()
283 .iter()
284 .map(|q| q.clone().unwrap_qubit().0)
285 .collect::<Vec<_>>();
286 let q = self.sim.allocate();
287 // The new qubit is by-definition in the |0⟩ state, so by reversing the sign of the
288 // angle we can apply the phase to the entire state without increasing its size in memory.
289 self.sim
290 .mcrz(&ctls, -2.0 * theta.clone().unwrap_double(), q);
291 self.sim.release(q);
292 Some(Ok(Value::unit()))
293 }
294 "BeginEstimateCaching" => Some(Ok(Value::Bool(true))),
295 "EndEstimateCaching"
296 | "AccountForEstimatesInternal"
297 | "BeginRepeatEstimatesInternal"
298 | "EndRepeatEstimatesInternal" => Some(Ok(Value::unit())),
299 _ => None,
300 }
301 }
302
303 fn set_seed(&mut self, seed: Option<u64>) {
304 match seed {
305 Some(seed) => self.sim.set_rng_seed(seed),
306 None => self.sim.set_rng_seed(rand::thread_rng().next_u64()),
307 }
308 }
309}
310
311/// Simple struct that chains two backends together so that the chained
312/// backend is called before the main backend.
313/// For any intrinsics that return a value,
314/// the value returned by the chained backend is ignored.
315/// The value returned by the main backend is returned.
316pub struct Chain<T1, T2> {
317 pub main: T1,
318 pub chained: T2,
319}
320
321impl<T1, T2> Chain<T1, T2>
322where
323 T1: Backend,
324 T2: Backend,
325{
326 pub fn new(primary: T1, chained: T2) -> Chain<T1, T2> {
327 Chain {
328 main: primary,
329 chained,
330 }
331 }
332}
333
334impl<T1, T2> Backend for Chain<T1, T2>
335where
336 T1: Backend,
337 T2: Backend,
338{
339 type ResultType = T1::ResultType;
340
341 fn ccx(&mut self, ctl0: usize, ctl1: usize, q: usize) {
342 self.chained.ccx(ctl0, ctl1, q);
343 self.main.ccx(ctl0, ctl1, q);
344 }
345
346 fn cx(&mut self, ctl: usize, q: usize) {
347 self.chained.cx(ctl, q);
348 self.main.cx(ctl, q);
349 }
350
351 fn cy(&mut self, ctl: usize, q: usize) {
352 self.chained.cy(ctl, q);
353 self.main.cy(ctl, q);
354 }
355
356 fn cz(&mut self, ctl: usize, q: usize) {
357 self.chained.cz(ctl, q);
358 self.main.cz(ctl, q);
359 }
360
361 fn h(&mut self, q: usize) {
362 self.chained.h(q);
363 self.main.h(q);
364 }
365
366 fn m(&mut self, q: usize) -> Self::ResultType {
367 let _ = self.chained.m(q);
368 self.main.m(q)
369 }
370
371 fn mresetz(&mut self, q: usize) -> Self::ResultType {
372 let _ = self.chained.mresetz(q);
373 self.main.mresetz(q)
374 }
375
376 fn reset(&mut self, q: usize) {
377 self.chained.reset(q);
378 self.main.reset(q);
379 }
380
381 fn rx(&mut self, theta: f64, q: usize) {
382 self.chained.rx(theta, q);
383 self.main.rx(theta, q);
384 }
385
386 fn rxx(&mut self, theta: f64, q0: usize, q1: usize) {
387 self.chained.rxx(theta, q0, q1);
388 self.main.rxx(theta, q0, q1);
389 }
390
391 fn ry(&mut self, theta: f64, q: usize) {
392 self.chained.ry(theta, q);
393 self.main.ry(theta, q);
394 }
395
396 fn ryy(&mut self, theta: f64, q0: usize, q1: usize) {
397 self.chained.ryy(theta, q0, q1);
398 self.main.ryy(theta, q0, q1);
399 }
400
401 fn rz(&mut self, theta: f64, q: usize) {
402 self.chained.rz(theta, q);
403 self.main.rz(theta, q);
404 }
405
406 fn rzz(&mut self, theta: f64, q0: usize, q1: usize) {
407 self.chained.rzz(theta, q0, q1);
408 self.main.rzz(theta, q0, q1);
409 }
410
411 fn sadj(&mut self, q: usize) {
412 self.chained.sadj(q);
413 self.main.sadj(q);
414 }
415
416 fn s(&mut self, q: usize) {
417 self.chained.s(q);
418 self.main.s(q);
419 }
420
421 fn swap(&mut self, q0: usize, q1: usize) {
422 self.chained.swap(q0, q1);
423 self.main.swap(q0, q1);
424 }
425
426 fn tadj(&mut self, q: usize) {
427 self.chained.tadj(q);
428 self.main.tadj(q);
429 }
430
431 fn t(&mut self, q: usize) {
432 self.chained.t(q);
433 self.main.t(q);
434 }
435
436 fn x(&mut self, q: usize) {
437 self.chained.x(q);
438 self.main.x(q);
439 }
440
441 fn y(&mut self, q: usize) {
442 self.chained.y(q);
443 self.main.y(q);
444 }
445
446 fn z(&mut self, q: usize) {
447 self.chained.z(q);
448 self.main.z(q);
449 }
450
451 fn qubit_allocate(&mut self) -> usize {
452 // Warning: we use the qubit id allocated by the
453 // main backend, even for later calls into the chained
454 // backend. This is not an issue today, but could
455 // become an issue if the qubit ids differ between
456 // the two backends.
457 let _ = self.chained.qubit_allocate();
458 self.main.qubit_allocate()
459 }
460
461 fn qubit_release(&mut self, q: usize) {
462 self.chained.qubit_release(q);
463 self.main.qubit_release(q);
464 }
465
466 fn qubit_swap_id(&mut self, q0: usize, q1: usize) {
467 self.chained.qubit_swap_id(q0, q1);
468 self.main.qubit_swap_id(q0, q1);
469 }
470
471 fn capture_quantum_state(
472 &mut self,
473 ) -> (Vec<(num_bigint::BigUint, num_complex::Complex<f64>)>, usize) {
474 let _ = self.chained.capture_quantum_state();
475 self.main.capture_quantum_state()
476 }
477
478 fn qubit_is_zero(&mut self, q: usize) -> bool {
479 let _ = self.chained.qubit_is_zero(q);
480 self.main.qubit_is_zero(q)
481 }
482
483 fn custom_intrinsic(&mut self, name: &str, arg: Value) -> Option<Result<Value, String>> {
484 let _ = self.chained.custom_intrinsic(name, arg.clone());
485 self.main.custom_intrinsic(name, arg)
486 }
487
488 fn set_seed(&mut self, seed: Option<u64>) {
489 self.chained.set_seed(seed);
490 self.main.set_seed(seed);
491 }
492}
493