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