microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.2.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

compiler/qsc_codegen/src/qir_base.rs

580lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#[cfg(test)]
5mod tests;
6
7use num_bigint::BigUint;
8use num_complex::Complex;
9use qsc_data_structures::index_map::IndexMap;
10use qsc_eval::{
11 backend::Backend,
12 debug::{map_hir_package_to_fir, Frame},
13 eval,
14 output::GenericReceiver,
15 val::Value,
16 Env, Error,
17};
18use qsc_fir::fir;
19use qsc_frontend::compile::PackageStore;
20use qsc_hir::hir::{self};
21use rustc_hash::FxHashSet;
22use std::fmt::{Display, Write};
23
24/// # Errors
25///
26/// This function will return an error if execution was unable to complete.
27/// # Panics
28///
29/// This function will panic if compiler state is invalid or in out-of-memory conditions.
30pub fn generate_qir(
31 store: &PackageStore,
32 package: hir::PackageId,
33) -> std::result::Result<String, (Error, Vec<Frame>)> {
34 let mut fir_lowerer = qsc_eval::lower::Lowerer::new();
35 let mut fir_store = fir::PackageStore::new();
36 for (id, unit) in store {
37 fir_store.insert(
38 map_hir_package_to_fir(id),
39 fir_lowerer.lower_package(&unit.package),
40 );
41 }
42
43 let package = map_hir_package_to_fir(package);
44 let unit = fir_store.get(package);
45 let entry_expr = unit.entry.expect("package should have entry");
46
47 let mut sim = BaseProfSim::default();
48 let mut stdout = std::io::sink();
49 let mut out = GenericReceiver::new(&mut stdout);
50 let result = eval(
51 package,
52 None,
53 entry_expr.into(),
54 &fir_store,
55 &mut Env::default(),
56 &mut sim,
57 &mut out,
58 );
59 match result {
60 Ok(val) => Ok(sim.finish(&val)),
61 Err((err, stack)) => Err((err, stack)),
62 }
63}
64
65#[derive(Copy, Clone, Default)]
66struct HardwareId(usize);
67
68pub struct BaseProfSim {
69 next_meas_id: usize,
70 next_qubit_id: usize,
71 next_qubit_hardware_id: HardwareId,
72 qubit_map: IndexMap<usize, HardwareId>,
73 instrs: String,
74 measurements: String,
75 decls: String,
76 decl_names: FxHashSet<String>,
77}
78
79impl Default for BaseProfSim {
80 fn default() -> Self {
81 Self::new()
82 }
83}
84
85impl BaseProfSim {
86 #[must_use]
87 pub fn new() -> Self {
88 let mut sim = BaseProfSim {
89 next_meas_id: 0,
90 next_qubit_id: 0,
91 next_qubit_hardware_id: HardwareId::default(),
92 qubit_map: IndexMap::new(),
93 instrs: String::new(),
94 measurements: String::new(),
95 decls: String::new(),
96 decl_names: FxHashSet::default(),
97 };
98 sim.instrs.push_str(include_str!("./qir_base/prefix.ll"));
99 sim
100 }
101
102 #[must_use]
103 pub fn finish(mut self, val: &Value) -> String {
104 self.instrs.push_str(&self.measurements);
105 self.write_output_recording(val)
106 .expect("writing to string should succeed");
107
108 write!(
109 self.instrs,
110 include_str!("./qir_base/postfix.ll"),
111 self.decls, self.next_qubit_hardware_id.0, self.next_meas_id
112 )
113 .expect("writing to string should succeed");
114
115 self.instrs
116 }
117
118 #[must_use]
119 fn get_meas_id(&mut self) -> usize {
120 let id = self.next_meas_id;
121 self.next_meas_id += 1;
122 id
123 }
124
125 fn map(&mut self, qubit: usize) -> HardwareId {
126 if let Some(mapped) = self.qubit_map.get(qubit) {
127 *mapped
128 } else {
129 let mapped = self.next_qubit_hardware_id;
130 self.next_qubit_hardware_id.0 += 1;
131 self.qubit_map.insert(qubit, mapped);
132 mapped
133 }
134 }
135
136 fn write_output_recording(&mut self, val: &Value) -> std::fmt::Result {
137 match val {
138 Value::Array(arr) => {
139 self.write_array_recording(arr.len())?;
140 for val in arr.iter() {
141 self.write_output_recording(val)?;
142 }
143 }
144 Value::Result(r) => {
145 self.write_result_recording(r.unwrap_id());
146 }
147 Value::Tuple(tup) => {
148 self.write_tuple_recording(tup.len())?;
149 for val in tup.iter() {
150 self.write_output_recording(val)?;
151 }
152 }
153 _ => panic!("unexpected value type: {val:?}"),
154 }
155 Ok(())
156 }
157
158 fn write_result_recording(&mut self, res: usize) {
159 writeln!(
160 self.instrs,
161 " call void @__quantum__rt__result_record_output({}, i8* null)",
162 Result(res),
163 )
164 .expect("writing to string should succeed");
165 }
166
167 fn write_tuple_recording(&mut self, size: usize) -> std::fmt::Result {
168 writeln!(
169 self.instrs,
170 " call void @__quantum__rt__tuple_record_output(i64 {size}, i8* null)"
171 )
172 }
173
174 fn write_array_recording(&mut self, size: usize) -> std::fmt::Result {
175 writeln!(
176 self.instrs,
177 " call void @__quantum__rt__array_record_output(i64 {size}, i8* null)"
178 )
179 }
180
181 fn write_arg(&mut self, arg: &Value) -> std::result::Result<(), String> {
182 match arg {
183 Value::Qubit(q) => {
184 let q = self.map(q.0);
185 write!(self.instrs, "{}", Qubit(q))
186 }
187 Value::Double(d) => write!(self.instrs, "{}", Double(*d)),
188 Value::Bool(b) => write!(self.instrs, "{}", Bool(*b)),
189 Value::Int(i) => write!(self.instrs, "{}", Int(*i)),
190 _ => return Err(format!("unsupported argument type: {}", arg.type_name())),
191 }
192 .expect("writing to string should succeed");
193 Ok(())
194 }
195
196 fn write_decl_type(&mut self, ty: &Value) -> std::result::Result<(), String> {
197 match ty {
198 Value::Qubit(_) => write!(self.decls, "%Qubit*"),
199 Value::Double(_) => write!(self.decls, "double"),
200 Value::Bool(_) => write!(self.decls, "i1"),
201 Value::Int(_) => write!(self.decls, "i64"),
202 _ => return Err(format!("unsupported argument type: {}", ty.type_name())),
203 }
204 .expect("writing to string should succeed");
205 Ok(())
206 }
207
208 fn write_decl(&mut self, name: &str, arg: &Value) -> std::result::Result<(), String> {
209 if self.decl_names.insert(name.to_string()) {
210 write!(self.decls, "declare void @{name}(").expect("writing to string should succeed");
211 if let Value::Tuple(args) = arg {
212 if let Some((first, rest)) = args.split_first() {
213 self.write_decl_type(first)?;
214 for arg in rest {
215 write!(self.decls, ", ").expect("writing to string should succeed");
216 self.write_decl_type(arg)?;
217 }
218 }
219 } else {
220 self.write_decl_type(arg)?;
221 }
222 writeln!(self.decls, ")").expect("writing to string should succeed");
223 }
224
225 Ok(())
226 }
227}
228
229impl Backend for BaseProfSim {
230 type ResultType = usize;
231
232 fn ccx(&mut self, ctl0: usize, ctl1: usize, q: usize) {
233 let ctl0 = self.map(ctl0);
234 let ctl1 = self.map(ctl1);
235 let q = self.map(q);
236 writeln!(
237 self.instrs,
238 " call void @__quantum__qis__ccx__body({}, {}, {})",
239 Qubit(ctl0),
240 Qubit(ctl1),
241 Qubit(q)
242 )
243 .expect("writing to string should succeed");
244 }
245
246 fn cx(&mut self, ctl: usize, q: usize) {
247 let ctl = self.map(ctl);
248 let q = self.map(q);
249 writeln!(
250 self.instrs,
251 " call void @__quantum__qis__cx__body({}, {})",
252 Qubit(ctl),
253 Qubit(q),
254 )
255 .expect("writing to string should succeed");
256 }
257
258 fn cy(&mut self, ctl: usize, q: usize) {
259 let ctl = self.map(ctl);
260 let q = self.map(q);
261 writeln!(
262 self.instrs,
263 " call void @__quantum__qis__cy__body({}, {})",
264 Qubit(ctl),
265 Qubit(q),
266 )
267 .expect("writing to string should succeed");
268 }
269
270 fn cz(&mut self, ctl: usize, q: usize) {
271 let ctl = self.map(ctl);
272 let q = self.map(q);
273 writeln!(
274 self.instrs,
275 " call void @__quantum__qis__cz__body({}, {})",
276 Qubit(ctl),
277 Qubit(q),
278 )
279 .expect("writing to string should succeed");
280 }
281
282 fn h(&mut self, q: usize) {
283 let q = self.map(q);
284 writeln!(
285 self.instrs,
286 " call void @__quantum__qis__h__body({})",
287 Qubit(q),
288 )
289 .expect("writing to string should succeed");
290 }
291
292 fn m(&mut self, q: usize) -> Self::ResultType {
293 let mapped_q = self.map(q);
294 let id = self.get_meas_id();
295 // Measurements are tracked separately from instructions, so that they can be
296 // deferred until the end of the program.
297 writeln!(
298 self.measurements,
299 " call void @__quantum__qis__mz__body({}, {}) #1",
300 Qubit(mapped_q),
301 Result(id),
302 )
303 .expect("writing to string should succeed");
304 self.reset(q);
305 id
306 }
307
308 fn mresetz(&mut self, q: usize) -> Self::ResultType {
309 self.m(q)
310 }
311
312 fn reset(&mut self, q: usize) {
313 // Reset is a no-op in Base Profile, but does force qubit remapping so that future
314 // operations on the given qubit id are performed on a fresh qubit. Clear the entry in the map
315 // so it is known to require remapping on next use.
316 self.qubit_map.remove(q);
317 }
318
319 fn rx(&mut self, theta: f64, q: usize) {
320 let q = self.map(q);
321 writeln!(
322 self.instrs,
323 " call void @__quantum__qis__rx__body({}, {})",
324 Double(theta),
325 Qubit(q),
326 )
327 .expect("writing to string should succeed");
328 }
329
330 fn rxx(&mut self, theta: f64, q0: usize, q1: usize) {
331 let q0 = self.map(q0);
332 let q1 = self.map(q1);
333 writeln!(
334 self.instrs,
335 " call void @__quantum__qis__rxx__body({}, {}, {})",
336 Double(theta),
337 Qubit(q0),
338 Qubit(q1),
339 )
340 .expect("writing to string should succeed");
341 }
342
343 fn ry(&mut self, theta: f64, q: usize) {
344 let q = self.map(q);
345 writeln!(
346 self.instrs,
347 " call void @__quantum__qis__ry__body({}, {})",
348 Double(theta),
349 Qubit(q),
350 )
351 .expect("writing to string should succeed");
352 }
353
354 fn ryy(&mut self, theta: f64, q0: usize, q1: usize) {
355 let q0 = self.map(q0);
356 let q1 = self.map(q1);
357 writeln!(
358 self.instrs,
359 " call void @__quantum__qis__ryy__body({}, {}, {})",
360 Double(theta),
361 Qubit(q0),
362 Qubit(q1),
363 )
364 .expect("writing to string should succeed");
365 }
366
367 fn rz(&mut self, theta: f64, q: usize) {
368 let q = self.map(q);
369 writeln!(
370 self.instrs,
371 " call void @__quantum__qis__rz__body({}, {})",
372 Double(theta),
373 Qubit(q),
374 )
375 .expect("writing to string should succeed");
376 }
377
378 fn rzz(&mut self, theta: f64, q0: usize, q1: usize) {
379 let q0 = self.map(q0);
380 let q1 = self.map(q1);
381 writeln!(
382 self.instrs,
383 " call void @__quantum__qis__rzz__body({}, {}, {})",
384 Double(theta),
385 Qubit(q0),
386 Qubit(q1),
387 )
388 .expect("writing to string should succeed");
389 }
390
391 fn sadj(&mut self, q: usize) {
392 let q = self.map(q);
393 writeln!(
394 self.instrs,
395 " call void @__quantum__qis__s__adj({})",
396 Qubit(q),
397 )
398 .expect("writing to string should succeed");
399 }
400
401 fn s(&mut self, q: usize) {
402 let q = self.map(q);
403 writeln!(
404 self.instrs,
405 " call void @__quantum__qis__s__body({})",
406 Qubit(q),
407 )
408 .expect("writing to string should succeed");
409 }
410
411 fn swap(&mut self, q0: usize, q1: usize) {
412 let q0 = self.map(q0);
413 let q1 = self.map(q1);
414 writeln!(
415 self.instrs,
416 " call void @__quantum__qis__swap__body({}, {})",
417 Qubit(q0),
418 Qubit(q1),
419 )
420 .expect("writing to string should succeed");
421 }
422
423 fn tadj(&mut self, q: usize) {
424 let q = self.map(q);
425 writeln!(
426 self.instrs,
427 " call void @__quantum__qis__t__adj({})",
428 Qubit(q),
429 )
430 .expect("writing to string should succeed");
431 }
432
433 fn t(&mut self, q: usize) {
434 let q = self.map(q);
435 writeln!(
436 self.instrs,
437 " call void @__quantum__qis__t__body({})",
438 Qubit(q),
439 )
440 .expect("writing to string should succeed");
441 }
442
443 fn x(&mut self, q: usize) {
444 let q = self.map(q);
445 writeln!(
446 self.instrs,
447 " call void @__quantum__qis__x__body({})",
448 Qubit(q),
449 )
450 .expect("writing to string should succeed");
451 }
452
453 fn y(&mut self, q: usize) {
454 let q = self.map(q);
455 writeln!(
456 self.instrs,
457 " call void @__quantum__qis__y__body({})",
458 Qubit(q),
459 )
460 .expect("writing to string should succeed");
461 }
462
463 fn z(&mut self, q: usize) {
464 let q = self.map(q);
465 writeln!(
466 self.instrs,
467 " call void @__quantum__qis__z__body({})",
468 Qubit(q),
469 )
470 .expect("writing to string should succeed");
471 }
472
473 fn qubit_allocate(&mut self) -> usize {
474 let id = self.next_qubit_id;
475 self.next_qubit_id += 1;
476 let _ = self.map(id);
477 id
478 }
479
480 fn qubit_release(&mut self, _q: usize) {
481 self.next_qubit_id -= 1;
482 }
483
484 fn capture_quantum_state(&mut self) -> (Vec<(BigUint, Complex<f64>)>, usize) {
485 (Vec::new(), 0)
486 }
487
488 fn qubit_is_zero(&mut self, _q: usize) -> bool {
489 // Because `qubit_is_zero` is called on every qubit release, this must return
490 // true to avoid a panic.
491 true
492 }
493
494 fn custom_intrinsic(
495 &mut self,
496 name: &str,
497 arg: Value,
498 ) -> Option<std::result::Result<Value, String>> {
499 match self.write_decl(name, &arg) {
500 Ok(()) => {}
501 Err(e) => return Some(Err(e)),
502 }
503 write!(self.instrs, " call void @{name}(").expect("writing to string should succeed");
504
505 if let Value::Tuple(args) = arg {
506 if let Some((first, rest)) = args.split_first() {
507 match self.write_arg(first) {
508 Ok(()) => {}
509 Err(e) => return Some(Err(e)),
510 }
511 for arg in rest {
512 write!(self.instrs, ", ").expect("writing to string should succeed");
513 match self.write_arg(arg) {
514 Ok(()) => {}
515 Err(e) => return Some(Err(e)),
516 }
517 }
518 }
519 } else {
520 match self.write_arg(&arg) {
521 Ok(()) => {}
522 Err(e) => return Some(Err(e)),
523 }
524 }
525
526 writeln!(self.instrs, ")").expect("writing to string should succeed");
527 Some(Ok(Value::unit()))
528 }
529}
530
531struct Qubit(HardwareId);
532
533impl Display for Qubit {
534 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
535 write!(f, "%Qubit* inttoptr (i64 {} to %Qubit*)", self.0 .0)
536 }
537}
538
539struct Result(usize);
540
541impl Display for Result {
542 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
543 write!(f, "%Result* inttoptr (i64 {} to %Result*)", self.0)
544 }
545}
546
547struct Double(f64);
548
549impl Display for Double {
550 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
551 let v = self.0;
552 if (v.floor() - v.ceil()).abs() < f64::EPSILON {
553 // The value is a whole number, which requires at least one decimal point
554 // to differentiate it from an integer value.
555 write!(f, "double {v:.1}")
556 } else {
557 write!(f, "double {v}")
558 }
559 }
560}
561
562struct Bool(bool);
563
564impl Display for Bool {
565 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
566 if self.0 {
567 write!(f, "i1 true")
568 } else {
569 write!(f, "i1 false")
570 }
571 }
572}
573
574struct Int(i64);
575
576impl Display for Int {
577 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
578 write!(f, "i64 {}", self.0)
579 }
580}