microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
5d2b4dbbc88caff5c5d1bfc596d9d8360c8cc91c

Branches

Tags

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

Clone

HTTPS

Download ZIP

compiler/qsc_codegen/src/qir_base.rs

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