qir_backend/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#![deny(clippy::all, clippy::pedantic)]
5
6//! # QIR compliant backend for quantum simulation.
7//! This libary builds on top of the `qir_stdlib` to implement a full backend for simulation of QIR
8//! programs. This includes a broad set of quantum intrinsic operations for sparse state simulation,
9//! based on the design from
10//! <a href="https://arxiv.org/abs/2105.01533">Leveraging state sparsity for more efficient quantum simulations</a>.
11
12pub mod result_bool;
13
14pub mod exp;
15
16use bitvec::prelude::*;
17use num_bigint::BigUint;
18use num_complex::Complex64;
19use quantum_sparse_sim::QuantumSim;
20use std::cell::RefCell;
21use std::convert::TryInto;
22use std::ffi::c_char;
23use std::ffi::c_double;
24use std::ffi::{c_void, CString};
25use std::io::Write;
26use std::mem::size_of;
27
28use result_bool::{
29    __quantum__rt__result_equal, __quantum__rt__result_get_one, __quantum__rt__result_get_zero,
30};
31
32pub use qir_stdlib::{
33    arrays::*, bigints::*, callables::*, math::*, output_recording::*, range_support::*,
34    strings::*, tuples::*, *,
35};
36
37struct SimulatorState {
38    pub sim: QuantumSim,
39    pub res: BitVec,
40    pub max_qubit_id: usize,
41}
42
43thread_local! {
44    static SIM_STATE: RefCell<SimulatorState> = RefCell::new(SimulatorState {
45        sim: QuantumSim::default(),
46        res: bitvec![],
47        max_qubit_id: 0
48    });
49}
50
51/// Sets the seed for the pseudo-random number generator used during measurements.
52pub fn set_rng_seed(seed: u64) {
53    SIM_STATE.with(|sim_state| {
54        let state = &mut *sim_state.borrow_mut();
55        state.sim.set_rng_seed(seed);
56    });
57}
58
59/// Initializes the execution environment.
60#[no_mangle]
61pub extern "C" fn __quantum__rt__initialize(_: *mut c_char) {
62    SIM_STATE.with(|sim_state| {
63        let state = &mut *sim_state.borrow_mut();
64        // in order to continue using the same RNG, we need to reset the simulator
65        // and keep the same RNG
66        state.sim = QuantumSim::new(Some(state.sim.take_rng()));
67        state.res = bitvec![];
68        state.max_qubit_id = 0;
69    });
70}
71
72fn ensure_sufficient_qubits(sim: &mut QuantumSim, qubit_id: usize, max: &mut usize) {
73    while qubit_id + 1 > *max {
74        let _ = sim.allocate();
75        *max += 1;
76    }
77}
78
79/// Maps the given qubits from the given Pauli basis into the computational basis, returning the
80/// unwrapped `QirArray`s into a vector of matching Pauli and qubit id tuples.
81#[allow(clippy::cast_ptr_alignment)]
82unsafe fn map_to_z_basis(
83    state: &mut SimulatorState,
84    paulis: *const QirArray,
85    qubits: *const QirArray,
86) -> Vec<(Pauli, usize)> {
87    let paulis_size = __quantum__rt__array_get_size_1d(paulis);
88    let qubits_size = __quantum__rt__array_get_size_1d(qubits);
89    if paulis_size != qubits_size {
90        __quantum__rt__fail(__quantum__rt__string_create(
91            CString::new("Pauli array and Qubit array must be the same size.")
92                .expect("Unable to allocate memory for failure message string.")
93                .as_bytes_with_nul()
94                .as_ptr() as *mut c_char,
95        ));
96    }
97
98    let combined_list: Vec<(Pauli, usize)> = (0..paulis_size)
99        .filter_map(|index| {
100            let p =
101                *__quantum__rt__array_get_element_ptr_1d(paulis, index).cast::<Pauli>() as Pauli;
102            let q = *__quantum__rt__array_get_element_ptr_1d(qubits, index).cast::<*mut c_void>()
103                as usize;
104            if let Pauli::I = p {
105                None
106            } else {
107                ensure_sufficient_qubits(&mut state.sim, q, &mut state.max_qubit_id);
108                Some((p, q))
109            }
110        })
111        .collect();
112
113    for (pauli, qubit) in &combined_list {
114        match pauli {
115            Pauli::X => state.sim.h(*qubit),
116            Pauli::Y => {
117                state.sim.h(*qubit);
118                state.sim.s(*qubit);
119                state.sim.h(*qubit);
120            }
121            _ => (),
122        }
123    }
124
125    combined_list
126}
127
128/// Given a vector of Pauli and qubit id pairs, unmaps from the computational basis back into the given
129/// Pauli basis. This should be the adjoint of the `map_to_z_basis` operation.
130fn unmap_from_z_basis(state: &mut SimulatorState, combined_list: Vec<(Pauli, usize)>) {
131    for (pauli, qubit) in combined_list {
132        match pauli {
133            Pauli::X => state.sim.h(qubit),
134            Pauli::Y => {
135                state.sim.h(qubit);
136                state.sim.sadj(qubit);
137                state.sim.h(qubit);
138            }
139            _ => (),
140        }
141    }
142}
143
144macro_rules! single_qubit_gate {
145    ($(#[$meta:meta])*
146    $qir_name:ident, $gate:expr) => {
147        $(#[$meta])*
148        #[no_mangle]
149        pub extern "C" fn $qir_name(qubit: *mut c_void) {
150            SIM_STATE.with(|sim_state| {
151                let state = &mut *sim_state.borrow_mut();
152                ensure_sufficient_qubits(&mut state.sim, qubit as usize, &mut state.max_qubit_id);
153
154                $gate(&mut state.sim, qubit as usize);
155            });
156        }
157    };
158}
159
160single_qubit_gate!(
161    /// QIR API for performing the H gate on the given qubit.
162    __quantum__qis__h__body,
163    QuantumSim::h
164);
165single_qubit_gate!(
166    /// QIR API for performing the S gate on the given qubit.
167    __quantum__qis__s__body,
168    QuantumSim::s
169);
170single_qubit_gate!(
171    /// QIR API for performing the Adjoint S gate on the given qubit.
172    __quantum__qis__s__adj,
173    QuantumSim::sadj
174);
175single_qubit_gate!(
176    /// QIR API for performing the T gate on the given qubit.
177    __quantum__qis__t__body,
178    QuantumSim::t
179);
180single_qubit_gate!(
181    /// QIR API for performing the Adjoint T gate on the given qubit.
182    __quantum__qis__t__adj,
183    QuantumSim::tadj
184);
185single_qubit_gate!(
186    /// QIR API for performing the X gate on the given qubit.
187    __quantum__qis__x__body,
188    QuantumSim::x
189);
190single_qubit_gate!(
191    /// QIR API for performing the Y gate on the given qubit.
192    __quantum__qis__y__body,
193    QuantumSim::y
194);
195single_qubit_gate!(
196    /// QIR API for performing the Z gate on the given qubit.
197    __quantum__qis__z__body,
198    QuantumSim::z
199);
200
201macro_rules! controlled_qubit_gate {
202    ($(#[$meta:meta])*
203    $qir_name:ident, $gate:expr, 1) => {
204        $(#[$meta])*
205        #[no_mangle]
206        pub extern "C" fn $qir_name(control: *mut c_void, target: *mut c_void) {
207            SIM_STATE.with(|sim_state| {
208                let state = &mut *sim_state.borrow_mut();
209                ensure_sufficient_qubits(&mut state.sim, target as usize, &mut state.max_qubit_id);
210                ensure_sufficient_qubits(&mut state.sim, control as usize, &mut state.max_qubit_id);
211
212                $gate(&mut state.sim, &[control as usize], target as usize);
213            });
214        }
215    };
216
217    ($(#[$meta:meta])*
218    $qir_name:ident, $gate:expr, 2) => {
219        $(#[$meta])*
220        #[no_mangle]
221        pub extern "C" fn $qir_name(
222            control_1: *mut c_void,
223            control_2: *mut c_void,
224            target: *mut c_void,
225        ) {
226            SIM_STATE.with(|sim_state| {
227                let state = &mut *sim_state.borrow_mut();
228                ensure_sufficient_qubits(&mut state.sim, target as usize, &mut state.max_qubit_id);
229                ensure_sufficient_qubits(&mut state.sim, control_1 as usize, &mut state.max_qubit_id);
230                ensure_sufficient_qubits(&mut state.sim, control_2 as usize, &mut state.max_qubit_id);
231
232                $gate(&mut state.sim, &[control_1 as usize, control_2 as usize], target as usize);
233            });
234        }
235    };
236}
237
238controlled_qubit_gate!(
239    /// QIR API for performing the CNOT gate with the given qubits.
240    __quantum__qis__cnot__body,
241    QuantumSim::mcx,
242    1
243);
244controlled_qubit_gate!(
245    /// QIR API for performing the CNOT gate with the given qubits.
246    __quantum__qis__cx__body,
247    QuantumSim::mcx,
248    1
249);
250controlled_qubit_gate!(
251    /// QIR API for performing the CCNOT gate with the given qubits.
252    __quantum__qis__ccx__body,
253    QuantumSim::mcx,
254    2
255);
256controlled_qubit_gate!(
257    /// QIR API for performing the CY gate with the given qubits.
258    __quantum__qis__cy__body,
259    QuantumSim::mcy,
260    1
261);
262controlled_qubit_gate!(
263    /// QIR API for performing the CZ gate with the given qubits.
264    __quantum__qis__cz__body,
265    QuantumSim::mcz,
266    1
267);
268
269macro_rules! single_qubit_rotation {
270    ($(#[$meta:meta])*
271    $qir_name:ident, $gate:expr) => {
272        $(#[$meta])*
273        #[no_mangle]
274        pub extern "C" fn $qir_name(theta: c_double, qubit: *mut c_void) {
275            SIM_STATE.with(|sim_state| {
276                let state = &mut *sim_state.borrow_mut();
277                ensure_sufficient_qubits(&mut state.sim, qubit as usize, &mut state.max_qubit_id);
278
279                $gate(&mut state.sim, theta, qubit as usize);
280            });
281        }
282    };
283}
284
285single_qubit_rotation!(
286    /// QIR API for applying a Pauli-X rotation with the given angle and qubit.
287    __quantum__qis__rx__body,
288    QuantumSim::rx
289);
290single_qubit_rotation!(
291    /// QIR API for applying a Pauli-Y rotation with the given angle and qubit.
292    __quantum__qis__ry__body,
293    QuantumSim::ry
294);
295single_qubit_rotation!(
296    /// QIR API for applying a Pauli-Z rotation with the given angle and qubit.
297    __quantum__qis__rz__body,
298    QuantumSim::rz
299);
300
301macro_rules! multicontrolled_qubit_gate {
302    ($(#[$meta:meta])*
303    $qir_name:ident, $gate:expr) => {
304        $(#[$meta])*
305        /// # Safety
306        ///
307        /// This function should only be called with arrays and tuples created by the QIR runtime library.
308        #[no_mangle]
309        #[allow(clippy::cast_ptr_alignment)]
310        pub unsafe extern "C" fn $qir_name(ctls: *const QirArray, qubit: *mut c_void) {
311            SIM_STATE.with(|sim_state| {
312                let state = &mut *sim_state.borrow_mut();
313                ensure_sufficient_qubits(&mut state.sim, qubit as usize, &mut state.max_qubit_id);
314                let ctls_size = __quantum__rt__array_get_size_1d(ctls);
315                let ctls_list: Vec<usize> = (0..ctls_size)
316                    .map(|index| {
317                        let q = *__quantum__rt__array_get_element_ptr_1d(ctls, index)
318                            .cast::<*mut c_void>() as usize;
319                        ensure_sufficient_qubits(&mut state.sim, q, &mut state.max_qubit_id);
320                        q
321                    })
322                    .collect();
323
324                $gate(&mut state.sim, &ctls_list, qubit as usize);
325            });
326        }
327    };
328}
329
330multicontrolled_qubit_gate!(
331    /// QIR API for performing the multicontrolled H gate with the given qubits.
332    __quantum__qis__h__ctl,
333    QuantumSim::mch
334);
335multicontrolled_qubit_gate!(
336    /// QIR API for performing the multicontrolled S gate with the given qubits.
337    __quantum__qis__s__ctl,
338    QuantumSim::mcs
339);
340multicontrolled_qubit_gate!(
341    /// QIR API for performing the multicontrolled Adjoint S gate with the given qubits.
342    __quantum__qis__s__ctladj,
343    QuantumSim::mcsadj
344);
345multicontrolled_qubit_gate!(
346    /// QIR API for performing the multicontrolled T gate with the given qubits.
347    __quantum__qis__t__ctl,
348    QuantumSim::mct
349);
350multicontrolled_qubit_gate!(
351    /// QIR API for performing the multicontrolled Adjoint T gate with the given qubits.
352    __quantum__qis__t__ctladj,
353    QuantumSim::mctadj
354);
355multicontrolled_qubit_gate!(
356    /// QIR API for performing the multicontrolled X gate with the given qubits.
357    __quantum__qis__x__ctl,
358    QuantumSim::mcx
359);
360multicontrolled_qubit_gate!(
361    /// QIR API for performing the multicontrolled Y gate with the given qubits.
362    __quantum__qis__y__ctl,
363    QuantumSim::mcy
364);
365multicontrolled_qubit_gate!(
366    /// QIR API for performing the multicontrolled Z gate with the given qubits.
367    __quantum__qis__z__ctl,
368    QuantumSim::mcz
369);
370
371#[derive(Copy, Clone)]
372#[repr(C)]
373struct RotationArgs {
374    theta: c_double,
375    qubit: *mut c_void,
376}
377
378macro_rules! multicontrolled_qubit_rotation {
379    ($(#[$meta:meta])*
380    $qir_name:ident, $gate:expr) => {
381        $(#[$meta])*
382        /// # Safety
383        ///
384        /// This function should only be called with arrays and tuples created by the QIR runtime library.
385        #[no_mangle]
386        #[allow(clippy::cast_ptr_alignment)]
387        pub unsafe extern "C" fn $qir_name(
388            ctls: *const QirArray,
389            arg_tuple: *mut *const Vec<u8>,
390        ) {
391            SIM_STATE.with(|sim_state| {
392                let state = &mut *sim_state.borrow_mut();
393
394                let args = *arg_tuple.cast::<RotationArgs>();
395
396                ensure_sufficient_qubits(&mut state.sim, args.qubit as usize, &mut state.max_qubit_id);
397                let ctls_size = __quantum__rt__array_get_size_1d(ctls);
398                let ctls_list: Vec<usize> = (0..ctls_size)
399                    .map(|index| {
400                        let q = *__quantum__rt__array_get_element_ptr_1d(ctls, index)
401                            .cast::<*mut c_void>() as usize;
402                        ensure_sufficient_qubits(&mut state.sim, q, &mut state.max_qubit_id);
403                        q
404                    })
405                    .collect();
406
407                $gate(
408                    &mut state.sim,
409                    &ctls_list,
410                    args.theta,
411                    args.qubit as usize,
412                );
413            });
414        }
415    };
416}
417
418multicontrolled_qubit_rotation!(
419    /// QIR API for applying a multicontrolled Pauli-X rotation with the given angle and qubit.
420    __quantum__qis__rx__ctl,
421    QuantumSim::mcrx
422);
423multicontrolled_qubit_rotation!(
424    /// QIR API for applying a multicontrolled Pauli-Y rotation with the given angle and qubit.
425    __quantum__qis__ry__ctl,
426    QuantumSim::mcry
427);
428multicontrolled_qubit_rotation!(
429    /// QIR API for applying a multicontrolled Pauli-Z rotation with the given angle and qubit.
430    __quantum__qis__rz__ctl,
431    QuantumSim::mcrz
432);
433
434/// QIR API for performing the SX gate on the given qubit.
435#[no_mangle]
436pub extern "C" fn __quantum__qis__sx__body(qubit: *mut c_void) {
437    __quantum__qis__h__body(qubit);
438    __quantum__qis__s__body(qubit);
439    __quantum__qis__h__body(qubit);
440}
441
442/// QIR API for applying a joint rotation Pauli-Y rotation with the given angle for the two target qubit.
443#[no_mangle]
444pub extern "C" fn __quantum__qis__rxx__body(
445    theta: c_double,
446    qubit1: *mut c_void,
447    qubit2: *mut c_void,
448) {
449    __quantum__qis__h__body(qubit1);
450
451    __quantum__qis__h__body(qubit2);
452
453    __quantum__qis__rzz__body(theta, qubit1, qubit2);
454
455    __quantum__qis__h__body(qubit2);
456
457    __quantum__qis__h__body(qubit1);
458}
459
460/// QIR API for applying a joint rotation Pauli-Y rotation with the given angle for the two target qubit.
461#[no_mangle]
462pub extern "C" fn __quantum__qis__ryy__body(
463    theta: c_double,
464    qubit1: *mut c_void,
465    qubit2: *mut c_void,
466) {
467    __quantum__qis__h__body(qubit1);
468    __quantum__qis__s__body(qubit1);
469    __quantum__qis__h__body(qubit1);
470
471    __quantum__qis__h__body(qubit2);
472    __quantum__qis__s__body(qubit2);
473    __quantum__qis__h__body(qubit2);
474
475    __quantum__qis__rzz__body(theta, qubit1, qubit2);
476
477    __quantum__qis__h__body(qubit2);
478    __quantum__qis__s__adj(qubit2);
479    __quantum__qis__h__body(qubit2);
480
481    __quantum__qis__h__body(qubit1);
482    __quantum__qis__s__adj(qubit1);
483    __quantum__qis__h__body(qubit1);
484}
485
486/// QIR API for applying a joint rotation Pauli-Z rotation with the given angle for the two target qubit.
487#[no_mangle]
488pub extern "C" fn __quantum__qis__rzz__body(
489    theta: c_double,
490    qubit1: *mut c_void,
491    qubit2: *mut c_void,
492) {
493    __quantum__qis__cx__body(qubit2, qubit1);
494    __quantum__qis__rz__body(theta, qubit1);
495    __quantum__qis__cx__body(qubit2, qubit1);
496}
497
498/// QIR API for applying a rotation about the given Pauli axis with the given angle and qubit.
499#[no_mangle]
500pub extern "C" fn __quantum__qis__r__body(pauli: Pauli, theta: c_double, qubit: *mut c_void) {
501    match pauli {
502        Pauli::I => (),
503        Pauli::X => __quantum__qis__rx__body(theta, qubit),
504        Pauli::Y => __quantum__qis__ry__body(theta, qubit),
505        Pauli::Z => __quantum__qis__rz__body(theta, qubit),
506    }
507}
508
509/// QIR API for applying an adjoint rotation about the given Pauli axis with the given angle and qubit.
510#[no_mangle]
511pub extern "C" fn __quantum__qis__r__adj(pauli: Pauli, theta: c_double, qubit: *mut c_void) {
512    __quantum__qis__r__body(pauli, -theta, qubit);
513}
514
515#[derive(Copy, Clone)]
516#[repr(C)]
517struct PauliRotationArgs {
518    pauli: Pauli,
519    theta: c_double,
520    qubit: *mut c_void,
521}
522
523/// QIR API for applying a controlled rotation about the given Pauli axis with the given angle and qubit.
524/// # Safety
525///
526/// This function should only be called with arrays and tuples created by the QIR runtime library.
527#[allow(clippy::cast_ptr_alignment)]
528#[no_mangle]
529pub unsafe extern "C" fn __quantum__qis__r__ctl(
530    ctls: *const QirArray,
531    arg_tuple: *mut *const Vec<u8>,
532) {
533    let args = *arg_tuple.cast::<PauliRotationArgs>();
534    let rot_args = RotationArgs {
535        theta: args.theta,
536        qubit: args.qubit,
537    };
538    let rot_arg_tuple = __quantum__rt__tuple_create(size_of::<RotationArgs>() as u64);
539    *rot_arg_tuple.cast::<RotationArgs>() = rot_args;
540
541    match args.pauli {
542        Pauli::X => __quantum__qis__rx__ctl(ctls, rot_arg_tuple),
543        Pauli::Y => __quantum__qis__ry__ctl(ctls, rot_arg_tuple),
544        Pauli::Z => __quantum__qis__rz__ctl(ctls, rot_arg_tuple),
545        Pauli::I => {
546            if __quantum__rt__array_get_size_1d(ctls) > 0 {
547                SIM_STATE.with(|sim_state| {
548                    let state = &mut *sim_state.borrow_mut();
549
550                    ensure_sufficient_qubits(
551                        &mut state.sim,
552                        args.qubit as usize,
553                        &mut state.max_qubit_id,
554                    );
555                    let ctls_size = __quantum__rt__array_get_size_1d(ctls);
556                    let ctls_list: Vec<usize> = (0..ctls_size)
557                        .map(|index| {
558                            let q = *__quantum__rt__array_get_element_ptr_1d(ctls, index)
559                                .cast::<*mut c_void>() as usize;
560                            ensure_sufficient_qubits(&mut state.sim, q, &mut state.max_qubit_id);
561                            q
562                        })
563                        .collect();
564
565                    if let Some((head, rest)) = ctls_list.split_first() {
566                        state.sim.mcphase(
567                            rest,
568                            Complex64::exp(Complex64::new(0.0, -args.theta / 2.0)),
569                            *head,
570                        );
571                    }
572                });
573            }
574        }
575    }
576
577    __quantum__rt__tuple_update_reference_count(rot_arg_tuple, -1);
578}
579
580/// QIR API for applying an adjoint controlled rotation about the given Pauli axis with the given angle and qubit.
581/// # Safety
582///
583/// This function should only be called with arrays and tuples created by the QIR runtime library.
584#[no_mangle]
585pub unsafe extern "C" fn __quantum__qis__r__ctladj(
586    ctls: *const QirArray,
587    arg_tuple: *mut *const Vec<u8>,
588) {
589    let args = *arg_tuple.cast::<PauliRotationArgs>();
590    let new_args = PauliRotationArgs {
591        pauli: args.pauli,
592        theta: -args.theta,
593        qubit: args.qubit,
594    };
595    let new_arg_tuple = __quantum__rt__tuple_create(size_of::<PauliRotationArgs>() as u64);
596    *new_arg_tuple.cast::<PauliRotationArgs>() = new_args;
597    __quantum__qis__r__ctl(ctls, new_arg_tuple);
598    __quantum__rt__tuple_update_reference_count(new_arg_tuple, -1);
599}
600
601/// QIR API for applying a SWAP gate to the given qubits.
602#[no_mangle]
603pub extern "C" fn __quantum__qis__swap__body(qubit1: *mut c_void, qubit2: *mut c_void) {
604    SIM_STATE.with(|sim_state| {
605        let state = &mut *sim_state.borrow_mut();
606        ensure_sufficient_qubits(&mut state.sim, qubit1 as usize, &mut state.max_qubit_id);
607        ensure_sufficient_qubits(&mut state.sim, qubit2 as usize, &mut state.max_qubit_id);
608
609        state.sim.swap_qubit_ids(qubit1 as usize, qubit2 as usize);
610    });
611}
612
613/// QIR API for resetting the given qubit in the computational basis.
614#[no_mangle]
615pub extern "C" fn __quantum__qis__reset__body(qubit: *mut c_void) {
616    SIM_STATE.with(|sim_state| {
617        let state = &mut *sim_state.borrow_mut();
618        ensure_sufficient_qubits(&mut state.sim, qubit as usize, &mut state.max_qubit_id);
619
620        if state.sim.measure(qubit as usize) {
621            state.sim.x(qubit as usize);
622        }
623    });
624}
625
626/// QIR API for measuring the given qubit and storing the measured value with the given result identifier,
627/// then resetting it in the computational basis.
628#[allow(clippy::missing_panics_doc)]
629// reason="Panics can only occur if the result that was just collected is not found in the BitVec, which should not happen."
630#[no_mangle]
631pub extern "C" fn __quantum__qis__mresetz__body(qubit: *mut c_void, result: *mut c_void) {
632    SIM_STATE.with(|sim_state| {
633        let state = &mut *sim_state.borrow_mut();
634        let res_id = result as usize;
635        ensure_sufficient_qubits(&mut state.sim, qubit as usize, &mut state.max_qubit_id);
636
637        if state.res.len() < res_id + 1 {
638            state.res.resize(res_id + 1, false);
639        }
640
641        let res = state.sim.measure(qubit as usize);
642
643        if res {
644            state.sim.x(qubit as usize);
645        }
646
647        *state
648            .res
649            .get_mut(res_id)
650            .expect("Result with given id missing after expansion.") = res;
651    });
652}
653
654/// QIR API for measuring the given qubit in the computation basis and storing the measured value with the given result identifier.
655#[allow(clippy::missing_panics_doc)]
656// reason="Panics can only occur if the result index is not found in the BitVec after resizing, which should not happen."
657#[no_mangle]
658pub extern "C" fn __quantum__qis__mz__body(qubit: *mut c_void, result: *mut c_void) {
659    SIM_STATE.with(|sim_state| {
660        let state = &mut *sim_state.borrow_mut();
661        let res_id = result as usize;
662        ensure_sufficient_qubits(&mut state.sim, qubit as usize, &mut state.max_qubit_id);
663
664        if state.res.len() < res_id + 1 {
665            state.res.resize(res_id + 1, false);
666        }
667
668        *state
669            .res
670            .get_mut(res_id)
671            .expect("Result with given id missing after expansion.") =
672            state.sim.measure(qubit as usize);
673    });
674}
675
676/// QIR API that reads the Boolean value corresponding to the given result identifier, where true
677/// indicates a |1⟩ state and false indicates a |0⟩ state.
678#[allow(clippy::missing_panics_doc)]
679// reason="Panics can only occur if the result index is not found in the BitVec after resizing, which should not happen."
680#[no_mangle]
681pub extern "C" fn __quantum__qis__read_result__body(result: *mut c_void) -> bool {
682    SIM_STATE.with(|sim_state| {
683        let res = &mut sim_state.borrow_mut().res;
684        let res_id = result as usize;
685        if res.len() < res_id + 1 {
686            res.resize(res_id + 1, false);
687        }
688
689        let b = *res
690            .get(res_id)
691            .expect("Result with given id missing after expansion.");
692        b
693    })
694}
695
696/// QIR API that measures a given qubit in the computational basis, returning a runtime managed result value.
697#[no_mangle]
698pub extern "C" fn __quantum__qis__m__body(qubit: *mut c_void) -> *mut c_void {
699    SIM_STATE.with(|sim_state| {
700        let state = &mut *sim_state.borrow_mut();
701        ensure_sufficient_qubits(&mut state.sim, qubit as usize, &mut state.max_qubit_id);
702
703        if state.sim.measure(qubit as usize) {
704            __quantum__rt__result_get_one()
705        } else {
706            __quantum__rt__result_get_zero()
707        }
708    })
709}
710
711/// QIR API that performs joint measurement of the given qubits in the corresponding Pauli bases, returning the parity as a runtime managed result value.
712/// # Safety
713///
714/// This function should only be called with arrays created by the QIR runtime library.
715/// # Panics
716///
717/// This function will panic if the provided paulis and qubits arrays are not of the same size.
718#[allow(clippy::cast_ptr_alignment)]
719#[no_mangle]
720pub unsafe extern "C" fn __quantum__qis__measure__body(
721    paulis: *const QirArray,
722    qubits: *const QirArray,
723) -> *mut c_void {
724    SIM_STATE.with(|sim_state| {
725        let mut state = sim_state.borrow_mut();
726
727        let combined_list = map_to_z_basis(&mut state, paulis, qubits);
728
729        let res = state.sim.joint_measure(
730            &combined_list
731                .iter()
732                .map(|(_, q)| *q)
733                .collect::<Vec<usize>>(),
734        );
735
736        unmap_from_z_basis(&mut state, combined_list);
737
738        if res {
739            __quantum__rt__result_get_one()
740        } else {
741            __quantum__rt__result_get_zero()
742        }
743    })
744}
745
746/// Rust API for checking internal simulator state and returning true only if the given qubit is in exactly the |0⟩ state.
747pub fn qubit_is_zero(qubit: *mut c_void) -> bool {
748    SIM_STATE.with(|sim_state| {
749        let state = &mut *sim_state.borrow_mut();
750        ensure_sufficient_qubits(&mut state.sim, qubit as usize, &mut state.max_qubit_id);
751
752        state.sim.qubit_is_zero(qubit as usize)
753    })
754}
755
756/// QIR API for checking internal simulator state and verifying the probability of the given parity measurement result
757/// for the given qubits in the given Pauli bases is equal to the expected probability, within the given tolerance.
758/// # Safety
759///
760/// This function should only be called with arrays created by the QIR runtime library.
761#[no_mangle]
762pub unsafe extern "C" fn __quantum__qis__assertmeasurementprobability__body(
763    paulis: *const QirArray,
764    qubits: *const QirArray,
765    result: *mut c_void,
766    prob: c_double,
767    msg: *const CString,
768    tol: c_double,
769) {
770    SIM_STATE.with(|sim_state| {
771        let mut state = sim_state.borrow_mut();
772
773        let combined_list = map_to_z_basis(&mut state, paulis, qubits);
774
775        let mut actual_prob = state.sim.joint_probability(
776            &combined_list
777                .iter()
778                .map(|(_, q)| *q)
779                .collect::<Vec<usize>>(),
780        );
781
782        if __quantum__rt__result_equal(result, __quantum__rt__result_get_zero()) {
783            actual_prob = 1.0 - actual_prob;
784        }
785
786        if (actual_prob - prob).abs() > tol {
787            __quantum__rt__fail(msg);
788        }
789
790        unmap_from_z_basis(&mut state, combined_list);
791    });
792}
793
794#[derive(Copy, Clone)]
795#[repr(C)]
796struct AssertMeasurementProbabilityArgs {
797    paulis: *const QirArray,
798    qubits: *const QirArray,
799    result: *mut c_void,
800    prob: c_double,
801    msg: *const CString,
802    tol: c_double,
803}
804
805/// QIR API for checking internal simulator state and verifying the probability of the given parity measurement result
806/// for the given qubits in the given Pauli bases is equal to the expected probability, within the given tolerance.
807/// Note that control qubits are ignored.
808/// # Safety
809///
810/// This function should only be called with arrays created by the QIR runtime library.
811#[no_mangle]
812pub unsafe extern "C" fn __quantum__qis__assertmeasurementprobability__ctl(
813    _ctls: *const QirArray,
814    arg_tuple: *mut *const Vec<u8>,
815) {
816    let args = *arg_tuple.cast::<AssertMeasurementProbabilityArgs>();
817    __quantum__qis__assertmeasurementprobability__body(
818        args.paulis,
819        args.qubits,
820        args.result,
821        args.prob,
822        args.msg,
823        args.tol,
824    );
825}
826
827pub mod legacy_output {
828    use std::ffi::c_void;
829
830    use qir_stdlib::output_recording::record_output_str;
831
832    use crate::{
833        result_bool::{__quantum__rt__result_equal, __quantum__rt__result_get_one},
834        SIM_STATE,
835    };
836
837    #[allow(clippy::missing_panics_doc)]
838    // reason="Panics can only occur if the result index is not found in the BitVec after resizing, which should not happen."
839    #[allow(non_snake_case)]
840    pub extern "C" fn __quantum__rt__result_record_output(result: *mut c_void) {
841        SIM_STATE.with(|sim_state| {
842            let res = &mut sim_state.borrow_mut().res;
843            let res_id = result as usize;
844            let b = if res.is_empty() {
845                // No static measurements have been used, so default to dynamic handling.
846                __quantum__rt__result_equal(result, __quantum__rt__result_get_one())
847            } else {
848                if res.len() < res_id + 1 {
849                    res.resize(res_id + 1, false);
850                }
851                *res.get(res_id)
852                    .expect("Result with given id missing after expansion.")
853            };
854
855            record_output_str(&format!("RESULT\t{}", if b { "1" } else { "0" }))
856                .expect("Failed to write result output");
857        });
858    }
859}
860
861/// QIR API for recording the given result into the program output.
862#[allow(clippy::missing_panics_doc)]
863// reason="Panics can only occur if the result index is not found in the BitVec after resizing, which should not happen."
864/// # Safety
865/// This function will panic if the tag cannot be written to the output buffer.
866#[no_mangle]
867pub unsafe extern "C" fn __quantum__rt__result_record_output(
868    result: *mut c_void,
869    tag: *mut c_char,
870) {
871    SIM_STATE.with(|sim_state| {
872        let res = &mut sim_state.borrow_mut().res;
873        let res_id = result as usize;
874        let b = if res.is_empty() {
875            // No static measurements have been used, so default to dynamic handling.
876            __quantum__rt__result_equal(result, __quantum__rt__result_get_one())
877        } else {
878            if res.len() < res_id + 1 {
879                res.resize(res_id + 1, false);
880            }
881            *res.get(res_id)
882                .expect("Result with given id missing after expansion.")
883        };
884
885        let val: i64 = i64::from(b);
886        record_output("RESULT", &val, tag).expect("Failed to write result output");
887    });
888}
889
890/// QIR API that allocates the next available qubit in the simulation.
891#[no_mangle]
892pub extern "C" fn __quantum__rt__qubit_allocate() -> *mut c_void {
893    SIM_STATE.with(|sim_state| {
894        let mut state = sim_state.borrow_mut();
895        let qubit_id = state.sim.allocate();
896
897        // Increase the max qubit id global so that `ensure_sufficient_qubits` wont trigger more allocations.
898        // NOTE: static allocation and dynamic allocation shouldn't be used together, so this is safe to do.
899        state.max_qubit_id = state.max_qubit_id.max(qubit_id + 1);
900
901        qubit_id as *mut c_void
902    })
903}
904
905/// QIR API for allocating the given number of qubits in the simulation, returning them as a runtime managed array.
906/// # Panics
907/// This function will panic if the requested array size is too large to be described with the system pointer size.
908#[allow(clippy::cast_ptr_alignment)]
909#[no_mangle]
910pub extern "C" fn __quantum__rt__qubit_allocate_array(size: u64) -> *const QirArray {
911    let arr = __quantum__rt__array_create_1d(
912        size_of::<usize>()
913            .try_into()
914            .expect("System pointer size too large to be described with u32."),
915        size,
916    );
917    for index in 0..size {
918        unsafe {
919            let elem = __quantum__rt__array_get_element_ptr_1d(arr, index).cast::<*mut c_void>();
920            *elem = __quantum__rt__qubit_allocate();
921        }
922    }
923    arr
924}
925
926/// QIR API for releasing the given runtime managed qubit array.
927/// # Safety
928///
929/// This function should only be called with arrays created by `__quantum__rt__qubit_allocate_array`.
930#[allow(clippy::cast_ptr_alignment)]
931#[no_mangle]
932pub unsafe extern "C" fn __quantum__rt__qubit_release_array(arr: *const QirArray) {
933    for index in 0..__quantum__rt__array_get_size_1d(arr) {
934        let elem = __quantum__rt__array_get_element_ptr_1d(arr, index).cast::<*mut c_void>();
935        __quantum__rt__qubit_release(*elem);
936    }
937    __quantum__rt__array_update_alias_count(arr, -1);
938}
939
940/// QIR API for releasing the given qubit from the simulation.
941#[no_mangle]
942pub extern "C" fn __quantum__rt__qubit_release(qubit: *mut c_void) {
943    SIM_STATE.with(|sim_state| {
944        let mut state = sim_state.borrow_mut();
945        state.sim.release(qubit as usize);
946    });
947}
948
949/// QIR API for getting the string interpretation of a qubit identifier.
950/// # Panics
951/// This function will panic if memory cannot be allocated for the underyling string.
952#[no_mangle]
953pub extern "C" fn __quantum__rt__qubit_to_string(qubit: *mut c_void) -> *const CString {
954    unsafe {
955        __quantum__rt__string_create(
956            CString::new(format!("{}", qubit as usize))
957                .expect("Unable to allocate memory for qubit string.")
958                .as_bytes_with_nul()
959                .as_ptr() as *mut c_char,
960        )
961    }
962}
963
964/// Rust API for getting a snapshot of current quantum state. The state is a sorted copy of
965/// the current sparse state represented by a vector of pairs of indices and complex numbers along
966/// with the total number of currently allocated qubits to help in interpreting the state.
967#[must_use]
968pub fn capture_quantum_state() -> (Vec<(BigUint, Complex64)>, usize) {
969    SIM_STATE.with(|sim_state| {
970        let mut state = sim_state.borrow_mut();
971        state.sim.get_state()
972    })
973}
974
975/// QIR API for dumping full internal simulator state.
976/// # Panics
977/// This function will panic if the output buffer is not available.
978#[no_mangle]
979pub extern "C" fn __quantum__qis__dumpmachine__body(location: *mut c_void) {
980    if !location.is_null() {
981        unimplemented!("Dump to location is not implemented.")
982    }
983    SIM_STATE.with(|sim_state| {
984        let mut state = sim_state.borrow_mut();
985
986        if !state.res.is_empty() {
987            OUTPUT.with(|output| {
988                let mut output = output.borrow_mut();
989                output
990                    .write_fmt(format_args!("Global Results: {}", state.res))
991                    .expect("Failed to write global results");
992                output.write_newline();
993            });
994        }
995        OUTPUT.with(|output| {
996            let mut output = output.borrow_mut();
997            output
998                .write_all(state.sim.dump().as_bytes())
999                .expect("Failed to write simulator state");
1000        });
1001    });
1002}
1003
1004/// QIR API for the barrier operation. This is a no-op in simulation.
1005#[no_mangle]
1006pub extern "C" fn __quantum__qis__barrier__body() {
1007    // No-op
1008}
1009
1010#[cfg(test)]
1011mod tests {
1012    use std::{f64::consts::PI, ffi::c_void, ptr::null_mut};
1013
1014    use crate::{
1015        __quantum__qis__cnot__body, __quantum__qis__cx__body, __quantum__qis__cz__body,
1016        __quantum__qis__dumpmachine__body, __quantum__qis__h__body, __quantum__qis__m__body,
1017        __quantum__qis__mresetz__body, __quantum__qis__mz__body, __quantum__qis__read_result__body,
1018        __quantum__qis__rx__body, __quantum__qis__rxx__body, __quantum__qis__ry__body,
1019        __quantum__qis__ryy__body, __quantum__qis__rz__body, __quantum__qis__rzz__body,
1020        __quantum__qis__s__adj, __quantum__qis__s__body, __quantum__qis__x__body,
1021        __quantum__rt__qubit_allocate, __quantum__rt__qubit_allocate_array,
1022        __quantum__rt__qubit_release, __quantum__rt__qubit_release_array,
1023        __quantum__rt__result_equal, capture_quantum_state, map_to_z_basis, qubit_is_zero,
1024        result_bool::__quantum__rt__result_get_one, unmap_from_z_basis, SIM_STATE,
1025    };
1026    use num_bigint::BigUint;
1027    use qir_stdlib::{
1028        arrays::{
1029            __quantum__rt__array_create_1d, __quantum__rt__array_get_element_ptr_1d,
1030            __quantum__rt__array_update_reference_count,
1031        },
1032        Pauli,
1033    };
1034
1035    #[test]
1036    fn basic_test_static() {
1037        let q0 = 5 as *mut c_void;
1038        let r0 = std::ptr::null_mut();
1039        let r1 = 1 as *mut c_void;
1040        __quantum__qis__mz__body(q0, r0);
1041        assert!(!__quantum__qis__read_result__body(r0));
1042        __quantum__qis__x__body(q0);
1043        __quantum__qis__mz__body(q0, r1);
1044        assert!(__quantum__qis__read_result__body(r1));
1045        __quantum__qis__x__body(q0);
1046        __quantum__qis__mz__body(q0, r0);
1047        assert!(!__quantum__qis__read_result__body(r0));
1048        assert!(!__quantum__qis__read_result__body(3 as *mut c_void));
1049        __quantum__qis__dumpmachine__body(null_mut());
1050    }
1051
1052    #[allow(clippy::cast_ptr_alignment)]
1053    #[test]
1054    fn basic_test_dynamic() {
1055        let q1 = __quantum__rt__qubit_allocate();
1056        let q2 = __quantum__rt__qubit_allocate();
1057        __quantum__qis__h__body(q1);
1058        __quantum__qis__cnot__body(q1, q2);
1059        let r1 = __quantum__qis__m__body(q1);
1060        let r2 = __quantum__qis__m__body(q2);
1061        assert!(__quantum__rt__result_equal(r1, r2));
1062        __quantum__qis__dumpmachine__body(null_mut());
1063        __quantum__rt__qubit_release(q2);
1064        __quantum__rt__qubit_release(q1);
1065        let qs = __quantum__rt__qubit_allocate_array(4);
1066        unsafe {
1067            let q_elem = __quantum__rt__array_get_element_ptr_1d(qs, 3).cast::<*mut c_void>();
1068            __quantum__qis__x__body(*q_elem);
1069            __quantum__qis__dumpmachine__body(null_mut());
1070            let r = __quantum__qis__m__body(*q_elem);
1071            assert!(__quantum__rt__result_equal(
1072                r,
1073                __quantum__rt__result_get_one()
1074            ));
1075            __quantum__rt__qubit_release_array(qs);
1076        }
1077    }
1078
1079    #[test]
1080    fn test_qubit_is_zero() {
1081        let q0 = __quantum__rt__qubit_allocate();
1082        assert!(qubit_is_zero(q0));
1083        __quantum__qis__x__body(q0);
1084        assert!(!qubit_is_zero(q0));
1085        __quantum__qis__h__body(q0);
1086        assert!(!qubit_is_zero(q0));
1087        let r = __quantum__qis__m__body(q0);
1088        assert!(
1089            qubit_is_zero(q0) != __quantum__rt__result_equal(r, __quantum__rt__result_get_one())
1090        );
1091    }
1092
1093    #[allow(clippy::cast_ptr_alignment)]
1094    #[test]
1095    fn test_map_unmap_are_adjoint() {
1096        unsafe fn check_map_unmap(pauli: Pauli) {
1097            let check_qubit = __quantum__rt__qubit_allocate();
1098            let qubits = __quantum__rt__qubit_allocate_array(1);
1099            let q = *__quantum__rt__array_get_element_ptr_1d(qubits, 0).cast::<*mut c_void>();
1100            let paulis = __quantum__rt__array_create_1d(1, 1);
1101            *__quantum__rt__array_get_element_ptr_1d(paulis, 0).cast::<Pauli>() = pauli;
1102
1103            __quantum__qis__h__body(check_qubit);
1104            __quantum__qis__cnot__body(check_qubit, q);
1105
1106            SIM_STATE.with(|sim_state| {
1107                let state = &mut *sim_state.borrow_mut();
1108                let combined_list = map_to_z_basis(state, paulis, qubits);
1109                unmap_from_z_basis(state, combined_list);
1110            });
1111
1112            __quantum__qis__cnot__body(check_qubit, q);
1113            __quantum__qis__h__body(check_qubit);
1114
1115            assert!(qubit_is_zero(q));
1116            assert!(qubit_is_zero(check_qubit));
1117
1118            __quantum__rt__array_update_reference_count(paulis, -1);
1119            __quantum__rt__qubit_release_array(qubits);
1120            __quantum__rt__qubit_release(check_qubit);
1121        }
1122
1123        unsafe {
1124            check_map_unmap(Pauli::X);
1125            check_map_unmap(Pauli::Y);
1126            check_map_unmap(Pauli::Z);
1127        }
1128    }
1129
1130    #[allow(clippy::cast_ptr_alignment)]
1131    #[test]
1132    fn test_map_pauli_x() {
1133        let qubits = __quantum__rt__qubit_allocate_array(1);
1134        unsafe {
1135            let q = *__quantum__rt__array_get_element_ptr_1d(qubits, 0).cast::<*mut c_void>();
1136            let paulis = __quantum__rt__array_create_1d(1, 1);
1137            *__quantum__rt__array_get_element_ptr_1d(paulis, 0).cast::<Pauli>() = Pauli::X;
1138
1139            __quantum__qis__h__body(q);
1140
1141            SIM_STATE.with(|sim_state| {
1142                let state = &mut *sim_state.borrow_mut();
1143                let _ = map_to_z_basis(state, paulis, qubits);
1144            });
1145
1146            qubit_is_zero(q);
1147
1148            __quantum__rt__array_update_reference_count(paulis, -1);
1149            __quantum__rt__qubit_release_array(qubits);
1150        }
1151    }
1152
1153    #[allow(clippy::cast_ptr_alignment)]
1154    #[test]
1155    fn test_map_pauli_y() {
1156        let qubits = __quantum__rt__qubit_allocate_array(1);
1157        unsafe {
1158            let q = *__quantum__rt__array_get_element_ptr_1d(qubits, 0).cast::<*mut c_void>();
1159            let paulis = __quantum__rt__array_create_1d(1, 1);
1160            *__quantum__rt__array_get_element_ptr_1d(paulis, 0).cast::<Pauli>() = Pauli::Y;
1161
1162            __quantum__qis__h__body(q);
1163            __quantum__qis__s__adj(q);
1164            __quantum__qis__h__body(q);
1165
1166            SIM_STATE.with(|sim_state| {
1167                let state = &mut *sim_state.borrow_mut();
1168                let _ = map_to_z_basis(state, paulis, qubits);
1169            });
1170
1171            qubit_is_zero(q);
1172
1173            __quantum__rt__array_update_reference_count(paulis, -1);
1174            __quantum__rt__qubit_release_array(qubits);
1175        }
1176    }
1177
1178    #[allow(clippy::cast_ptr_alignment)]
1179    #[test]
1180    fn test_map_pauli_z() {
1181        let qubits = __quantum__rt__qubit_allocate_array(1);
1182        unsafe {
1183            let q = *__quantum__rt__array_get_element_ptr_1d(qubits, 0).cast::<*mut c_void>();
1184            let paulis = __quantum__rt__array_create_1d(1, 1);
1185            *__quantum__rt__array_get_element_ptr_1d(paulis, 0).cast::<Pauli>() = Pauli::Z;
1186
1187            SIM_STATE.with(|sim_state| {
1188                let state = &mut *sim_state.borrow_mut();
1189                let _ = map_to_z_basis(state, paulis, qubits);
1190            });
1191
1192            qubit_is_zero(q);
1193
1194            __quantum__rt__array_update_reference_count(paulis, -1);
1195            __quantum__rt__qubit_release_array(qubits);
1196        }
1197    }
1198
1199    #[test]
1200    fn test_joint_zz() {
1201        let check_qubit = __quantum__rt__qubit_allocate();
1202        let q0 = __quantum__rt__qubit_allocate();
1203        let q1 = __quantum__rt__qubit_allocate();
1204
1205        __quantum__qis__h__body(check_qubit);
1206        __quantum__qis__cx__body(check_qubit, q0);
1207        __quantum__qis__cx__body(check_qubit, q1);
1208
1209        __quantum__qis__rzz__body(PI / 2.0, q0, q1);
1210        __quantum__qis__rz__body(-PI / 2.0, q0);
1211        __quantum__qis__rz__body(-PI / 2.0, q1);
1212
1213        __quantum__qis__cz__body(q0, q1);
1214
1215        __quantum__qis__cx__body(check_qubit, q1);
1216        __quantum__qis__cx__body(check_qubit, q0);
1217        __quantum__qis__h__body(check_qubit);
1218
1219        assert!(qubit_is_zero(check_qubit));
1220        assert!(qubit_is_zero(q0));
1221        assert!(qubit_is_zero(q1));
1222    }
1223
1224    #[test]
1225    fn test_joint_yy() {
1226        let check_qubit = __quantum__rt__qubit_allocate();
1227        let q0 = __quantum__rt__qubit_allocate();
1228        let q1 = __quantum__rt__qubit_allocate();
1229
1230        __quantum__qis__h__body(check_qubit);
1231        __quantum__qis__cx__body(check_qubit, q0);
1232        __quantum__qis__cx__body(check_qubit, q1);
1233
1234        __quantum__qis__h__body(q0);
1235        __quantum__qis__s__adj(q0);
1236        __quantum__qis__h__body(q0);
1237        __quantum__qis__h__body(q1);
1238        __quantum__qis__s__adj(q1);
1239        __quantum__qis__h__body(q1);
1240
1241        __quantum__qis__ryy__body(PI / 2.0, q0, q1);
1242        __quantum__qis__ry__body(-PI / 2.0, q0);
1243        __quantum__qis__ry__body(-PI / 2.0, q1);
1244
1245        __quantum__qis__h__body(q1);
1246        __quantum__qis__s__body(q1);
1247        __quantum__qis__h__body(q1);
1248        __quantum__qis__h__body(q0);
1249        __quantum__qis__s__body(q0);
1250        __quantum__qis__h__body(q0);
1251
1252        __quantum__qis__cz__body(q0, q1);
1253
1254        __quantum__qis__cx__body(check_qubit, q1);
1255        __quantum__qis__cx__body(check_qubit, q0);
1256        __quantum__qis__h__body(check_qubit);
1257
1258        assert!(qubit_is_zero(check_qubit));
1259        assert!(qubit_is_zero(q0));
1260        assert!(qubit_is_zero(q1));
1261    }
1262
1263    #[test]
1264    fn test_joint_xx() {
1265        let check_qubit = __quantum__rt__qubit_allocate();
1266        let q0 = __quantum__rt__qubit_allocate();
1267        let q1 = __quantum__rt__qubit_allocate();
1268
1269        __quantum__qis__h__body(check_qubit);
1270        __quantum__qis__cx__body(check_qubit, q0);
1271        __quantum__qis__cx__body(check_qubit, q1);
1272
1273        __quantum__qis__h__body(q0);
1274        __quantum__qis__h__body(q1);
1275
1276        __quantum__qis__rxx__body(PI / 2.0, q0, q1);
1277        __quantum__qis__rx__body(-PI / 2.0, q0);
1278        __quantum__qis__rx__body(-PI / 2.0, q1);
1279
1280        __quantum__qis__h__body(q0);
1281        __quantum__qis__h__body(q1);
1282
1283        __quantum__qis__cz__body(q0, q1);
1284
1285        __quantum__qis__cx__body(check_qubit, q1);
1286        __quantum__qis__cx__body(check_qubit, q0);
1287        __quantum__qis__h__body(check_qubit);
1288
1289        assert!(qubit_is_zero(check_qubit));
1290        assert!(qubit_is_zero(q0));
1291        assert!(qubit_is_zero(q1));
1292    }
1293
1294    #[test]
1295    fn test_mresetz() {
1296        let qubit = __quantum__rt__qubit_allocate();
1297        let r0 = std::ptr::null_mut();
1298        let r1 = 1 as *mut c_void;
1299        assert!(qubit_is_zero(qubit));
1300        __quantum__qis__mresetz__body(qubit, r0);
1301        assert!(!__quantum__qis__read_result__body(r0));
1302        assert!(qubit_is_zero(qubit));
1303        __quantum__qis__x__body(qubit);
1304        __quantum__qis__mresetz__body(qubit, r1);
1305        assert!(__quantum__qis__read_result__body(r1));
1306        assert!(qubit_is_zero(qubit));
1307    }
1308
1309    #[test]
1310    fn test_capture_quantum_state() {
1311        let qubit = __quantum__rt__qubit_allocate();
1312        let (state, qubit_count) = capture_quantum_state();
1313        assert_eq!(qubit_count, 1);
1314        assert_eq!(state.len(), 1);
1315        assert_eq!(state[0].0, BigUint::from(0u32));
1316        __quantum__qis__x__body(qubit);
1317        let (state, qubit_count) = capture_quantum_state();
1318        assert_eq!(qubit_count, 1);
1319        assert_eq!(state.len(), 1);
1320        assert_eq!(state[0].0, BigUint::from(1u32));
1321        __quantum__qis__h__body(qubit);
1322        let qubit2 = __quantum__rt__qubit_allocate();
1323        let (state, qubit_count) = capture_quantum_state();
1324        assert_eq!(qubit_count, 2);
1325        assert_eq!(state.len(), 2);
1326        assert_eq!(state[0].1, -state[1].1);
1327        __quantum__qis__h__body(qubit);
1328        __quantum__qis__x__body(qubit);
1329        let (state, qubit_count) = capture_quantum_state();
1330        assert_eq!(qubit_count, 2);
1331        assert_eq!(state.len(), 1);
1332        assert_eq!(state[0].0, BigUint::from(0u32));
1333        __quantum__rt__qubit_release(qubit);
1334        __quantum__rt__qubit_release(qubit2);
1335        let (state, qubit_count) = capture_quantum_state();
1336        assert_eq!(qubit_count, 0);
1337        assert_eq!(state.len(), 1);
1338        assert_eq!(state[0].0, BigUint::from(0u32));
1339    }
1340}