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 writes the given Boolean value to the given result identifier, overwriting any previous value stored there.
715#[allow(clippy::missing_panics_doc)]
716// reason="Panics can only occur if the result index is not found in the BitVec after resizing, which should not happen."
717#[unsafe(no_mangle)]
718pub extern "C" fn __quantum__rt__write_result(value: bool, result: *mut c_void) {
719    SIM_STATE.with(|sim_state| {
720        let res = &mut sim_state.borrow_mut().res;
721        let res_id = result as usize;
722        if res.len() < res_id + 1 {
723            res.resize(res_id + 1, false);
724        }
725
726        *res.get_mut(res_id)
727            .expect("Result with given id missing after expansion.") = value;
728    });
729}
730
731/// QIR API that measures a given qubit in the computational basis, returning a runtime managed result value.
732#[unsafe(no_mangle)]
733pub extern "C" fn __quantum__qis__m__body(qubit: *mut c_void) -> *mut c_void {
734    SIM_STATE.with(|sim_state| {
735        let state = &mut *sim_state.borrow_mut();
736        ensure_sufficient_qubits(&mut state.sim, qubit as usize, &mut state.max_qubit_id);
737
738        if state.sim.measure(qubit as usize) {
739            __quantum__rt__result_get_one()
740        } else {
741            __quantum__rt__result_get_zero()
742        }
743    })
744}
745
746/// QIR API that performs joint measurement of the given qubits in the corresponding Pauli bases, returning the parity as a runtime managed result value.
747/// # Safety
748///
749/// This function should only be called with arrays created by the QIR runtime library.
750/// # Panics
751///
752/// This function will panic if the provided paulis and qubits arrays are not of the same size.
753#[allow(clippy::cast_ptr_alignment)]
754#[unsafe(no_mangle)]
755pub unsafe extern "C" fn __quantum__qis__measure__body(
756    paulis: *const QirArray,
757    qubits: *const QirArray,
758) -> *mut c_void {
759    unsafe {
760        SIM_STATE.with(|sim_state| {
761            let mut state = sim_state.borrow_mut();
762
763            let combined_list = map_to_z_basis(&mut state, paulis, qubits);
764
765            let res = state.sim.joint_measure(
766                &combined_list
767                    .iter()
768                    .map(|(_, q)| *q)
769                    .collect::<Vec<usize>>(),
770            );
771
772            unmap_from_z_basis(&mut state, combined_list);
773
774            if res {
775                __quantum__rt__result_get_one()
776            } else {
777                __quantum__rt__result_get_zero()
778            }
779        })
780    }
781}
782
783/// Rust API for checking internal simulator state and returning true only if the given qubit is in exactly the |0⟩ state.
784pub fn qubit_is_zero(qubit: *mut c_void) -> bool {
785    SIM_STATE.with(|sim_state| {
786        let state = &mut *sim_state.borrow_mut();
787        ensure_sufficient_qubits(&mut state.sim, qubit as usize, &mut state.max_qubit_id);
788
789        state.sim.qubit_is_zero(qubit as usize)
790    })
791}
792
793/// QIR API for checking internal simulator state and verifying the probability of the given parity measurement result
794/// for the given qubits in the given Pauli bases is equal to the expected probability, within the given tolerance.
795/// # Safety
796///
797/// This function should only be called with arrays created by the QIR runtime library.
798#[unsafe(no_mangle)]
799pub unsafe extern "C" fn __quantum__qis__assertmeasurementprobability__body(
800    paulis: *const QirArray,
801    qubits: *const QirArray,
802    result: *mut c_void,
803    prob: c_double,
804    msg: *const CString,
805    tol: c_double,
806) {
807    unsafe {
808        SIM_STATE.with(|sim_state| {
809            let mut state = sim_state.borrow_mut();
810
811            let combined_list = map_to_z_basis(&mut state, paulis, qubits);
812
813            let mut actual_prob = state.sim.joint_probability(
814                &combined_list
815                    .iter()
816                    .map(|(_, q)| *q)
817                    .collect::<Vec<usize>>(),
818            );
819
820            if __quantum__rt__result_equal(result, __quantum__rt__result_get_zero()) {
821                actual_prob = 1.0 - actual_prob;
822            }
823
824            if (actual_prob - prob).abs() > tol {
825                __quantum__rt__fail(msg);
826            }
827
828            unmap_from_z_basis(&mut state, combined_list);
829        });
830    }
831}
832
833#[derive(Copy, Clone)]
834#[repr(C)]
835struct AssertMeasurementProbabilityArgs {
836    paulis: *const QirArray,
837    qubits: *const QirArray,
838    result: *mut c_void,
839    prob: c_double,
840    msg: *const CString,
841    tol: c_double,
842}
843
844/// QIR API for checking internal simulator state and verifying the probability of the given parity measurement result
845/// for the given qubits in the given Pauli bases is equal to the expected probability, within the given tolerance.
846/// Note that control qubits are ignored.
847/// # Safety
848///
849/// This function should only be called with arrays created by the QIR runtime library.
850#[unsafe(no_mangle)]
851pub unsafe extern "C" fn __quantum__qis__assertmeasurementprobability__ctl(
852    _ctls: *const QirArray,
853    arg_tuple: *mut *const Vec<u8>,
854) {
855    unsafe {
856        let args = *arg_tuple.cast::<AssertMeasurementProbabilityArgs>();
857        __quantum__qis__assertmeasurementprobability__body(
858            args.paulis,
859            args.qubits,
860            args.result,
861            args.prob,
862            args.msg,
863            args.tol,
864        );
865    }
866}
867
868pub mod legacy_output {
869    use std::ffi::c_void;
870
871    use qir_stdlib::output_recording::record_output_str;
872
873    use crate::{
874        SIM_STATE,
875        result_bool::{__quantum__rt__result_equal, __quantum__rt__result_get_one},
876    };
877
878    #[allow(clippy::missing_panics_doc)]
879    // reason="Panics can only occur if the result index is not found in the BitVec after resizing, which should not happen."
880    #[allow(non_snake_case)]
881    pub extern "C" fn __quantum__rt__result_record_output(result: *mut c_void) {
882        SIM_STATE.with(|sim_state| {
883            let res = &mut sim_state.borrow_mut().res;
884            let res_id = result as usize;
885            let b = if res.is_empty() {
886                // No static measurements have been used, so default to dynamic handling.
887                __quantum__rt__result_equal(result, __quantum__rt__result_get_one())
888            } else {
889                if res.len() < res_id + 1 {
890                    res.resize(res_id + 1, false);
891                }
892                *res.get(res_id)
893                    .expect("Result with given id missing after expansion.")
894            };
895
896            record_output_str(&format!("RESULT\t{}", if b { "1" } else { "0" }))
897                .expect("Failed to write result output");
898        });
899    }
900}
901
902/// QIR API for recording the given result into the program output.
903#[allow(clippy::missing_panics_doc)]
904// reason="Panics can only occur if the result index is not found in the BitVec after resizing, which should not happen."
905/// # Safety
906/// This function will panic if the tag cannot be written to the output buffer.
907#[unsafe(no_mangle)]
908pub unsafe extern "C" fn __quantum__rt__result_record_output(
909    result: *mut c_void,
910    tag: *mut c_char,
911) {
912    unsafe {
913        SIM_STATE.with(|sim_state| {
914            let res = &mut sim_state.borrow_mut().res;
915            let res_id = result as usize;
916            let b = if res.is_empty() {
917                // No static measurements have been used, so default to dynamic handling.
918                __quantum__rt__result_equal(result, __quantum__rt__result_get_one())
919            } else {
920                if res.len() < res_id + 1 {
921                    res.resize(res_id + 1, false);
922                }
923                *res.get(res_id)
924                    .expect("Result with given id missing after expansion.")
925            };
926
927            let val: i64 = i64::from(b);
928            record_output("RESULT", &val, tag).expect("Failed to write result output");
929        });
930    }
931}
932
933/// QIR API that allocates the next available qubit in the simulation.
934#[unsafe(no_mangle)]
935pub extern "C" fn __quantum__rt__qubit_allocate() -> *mut c_void {
936    SIM_STATE.with(|sim_state| {
937        let mut state = sim_state.borrow_mut();
938        let qubit_id = state.sim.allocate();
939
940        // Increase the max qubit id global so that `ensure_sufficient_qubits` wont trigger more allocations.
941        // NOTE: static allocation and dynamic allocation shouldn't be used together, so this is safe to do.
942        state.max_qubit_id = state.max_qubit_id.max(qubit_id + 1);
943
944        qubit_id as *mut c_void
945    })
946}
947
948/// QIR API for allocating the given number of qubits in the simulation, returning them as a runtime managed array.
949/// # Panics
950/// This function will panic if the requested array size is too large to be described with the system pointer size.
951#[allow(clippy::cast_ptr_alignment)]
952#[unsafe(no_mangle)]
953pub extern "C" fn __quantum__rt__qubit_allocate_array(size: u64) -> *const QirArray {
954    let arr = __quantum__rt__array_create_1d(
955        size_of::<usize>()
956            .try_into()
957            .expect("System pointer size too large to be described with u32."),
958        size,
959    );
960    for index in 0..size {
961        unsafe {
962            let elem = __quantum__rt__array_get_element_ptr_1d(arr, index).cast::<*mut c_void>();
963            *elem = __quantum__rt__qubit_allocate();
964        }
965    }
966    arr
967}
968
969/// QIR API for releasing the given runtime managed qubit array.
970/// # Safety
971///
972/// This function should only be called with arrays created by `__quantum__rt__qubit_allocate_array`.
973#[allow(clippy::cast_ptr_alignment)]
974#[unsafe(no_mangle)]
975pub unsafe extern "C" fn __quantum__rt__qubit_release_array(arr: *const QirArray) {
976    unsafe {
977        for index in 0..__quantum__rt__array_get_size_1d(arr) {
978            let elem = __quantum__rt__array_get_element_ptr_1d(arr, index).cast::<*mut c_void>();
979            __quantum__rt__qubit_release(*elem);
980        }
981        __quantum__rt__array_update_alias_count(arr, -1);
982    }
983}
984
985/// QIR API for releasing the given qubit from the simulation.
986#[unsafe(no_mangle)]
987pub extern "C" fn __quantum__rt__qubit_release(qubit: *mut c_void) {
988    SIM_STATE.with(|sim_state| {
989        let mut state = sim_state.borrow_mut();
990        state.sim.release(qubit as usize);
991    });
992}
993
994/// QIR API for getting the string interpretation of a qubit identifier.
995/// # Panics
996/// This function will panic if memory cannot be allocated for the underyling string.
997#[unsafe(no_mangle)]
998pub extern "C" fn __quantum__rt__qubit_to_string(qubit: *mut c_void) -> *const CString {
999    unsafe {
1000        __quantum__rt__string_create(
1001            CString::new(format!("{}", qubit as usize))
1002                .expect("Unable to allocate memory for qubit string.")
1003                .as_bytes_with_nul()
1004                .as_ptr() as *mut c_char,
1005        )
1006    }
1007}
1008
1009/// Rust API for getting a snapshot of current quantum state. The state is a sorted copy of
1010/// the current sparse state represented by a vector of pairs of indices and complex numbers along
1011/// with the total number of currently allocated qubits to help in interpreting the state.
1012#[must_use]
1013pub fn capture_quantum_state() -> (Vec<(BigUint, Complex64)>, usize) {
1014    SIM_STATE.with(|sim_state| {
1015        let mut state = sim_state.borrow_mut();
1016        state.sim.get_state()
1017    })
1018}
1019
1020/// QIR API for dumping full internal simulator state.
1021/// # Panics
1022/// This function will panic if the output buffer is not available.
1023#[unsafe(no_mangle)]
1024pub extern "C" fn __quantum__qis__dumpmachine__body(location: *mut c_void) {
1025    if !location.is_null() {
1026        unimplemented!("Dump to location is not implemented.")
1027    }
1028    SIM_STATE.with(|sim_state| {
1029        let mut state = sim_state.borrow_mut();
1030
1031        if !state.res.is_empty() {
1032            OUTPUT.with(|output| {
1033                let mut output = output.borrow_mut();
1034                output
1035                    .write_fmt(format_args!("Global Results: {}", state.res))
1036                    .expect("Failed to write global results");
1037                output.write_newline();
1038            });
1039        }
1040        OUTPUT.with(|output| {
1041            let mut output = output.borrow_mut();
1042            output
1043                .write_all(state.sim.dump().as_bytes())
1044                .expect("Failed to write simulator state");
1045        });
1046    });
1047}
1048
1049/// QIR API for the barrier operation. This is a no-op in simulation.
1050#[unsafe(no_mangle)]
1051pub extern "C" fn __quantum__qis__barrier__body() {
1052    // No-op
1053}
1054
1055#[cfg(test)]
1056#[allow(clippy::manual_dangling_ptr)]
1057mod tests {
1058    use std::{f64::consts::PI, ffi::c_void, ptr::null_mut};
1059
1060    use crate::{
1061        __quantum__qis__cnot__body, __quantum__qis__cx__body, __quantum__qis__cz__body,
1062        __quantum__qis__dumpmachine__body, __quantum__qis__h__body, __quantum__qis__m__body,
1063        __quantum__qis__mresetz__body, __quantum__qis__mz__body, __quantum__qis__read_result__body,
1064        __quantum__qis__rx__body, __quantum__qis__rxx__body, __quantum__qis__ry__body,
1065        __quantum__qis__ryy__body, __quantum__qis__rz__body, __quantum__qis__rzz__body,
1066        __quantum__qis__s__adj, __quantum__qis__s__body, __quantum__qis__x__body,
1067        __quantum__rt__qubit_allocate, __quantum__rt__qubit_allocate_array,
1068        __quantum__rt__qubit_release, __quantum__rt__qubit_release_array,
1069        __quantum__rt__result_equal, SIM_STATE, capture_quantum_state, map_to_z_basis,
1070        qubit_is_zero, result_bool::__quantum__rt__result_get_one, unmap_from_z_basis,
1071    };
1072    use num_bigint::BigUint;
1073    use qir_stdlib::{
1074        Pauli,
1075        arrays::{
1076            __quantum__rt__array_create_1d, __quantum__rt__array_get_element_ptr_1d,
1077            __quantum__rt__array_update_reference_count,
1078        },
1079    };
1080
1081    #[test]
1082    fn basic_test_static() {
1083        let q0 = 5 as *mut c_void;
1084        let r0 = std::ptr::null_mut();
1085        let r1 = 1 as *mut c_void;
1086        __quantum__qis__mz__body(q0, r0);
1087        assert!(!__quantum__qis__read_result__body(r0));
1088        __quantum__qis__x__body(q0);
1089        __quantum__qis__mz__body(q0, r1);
1090        assert!(__quantum__qis__read_result__body(r1));
1091        __quantum__qis__x__body(q0);
1092        __quantum__qis__mz__body(q0, r0);
1093        assert!(!__quantum__qis__read_result__body(r0));
1094        assert!(!__quantum__qis__read_result__body(3 as *mut c_void));
1095        __quantum__qis__dumpmachine__body(null_mut());
1096    }
1097
1098    #[allow(clippy::cast_ptr_alignment)]
1099    #[test]
1100    fn basic_test_dynamic() {
1101        let q1 = __quantum__rt__qubit_allocate();
1102        let q2 = __quantum__rt__qubit_allocate();
1103        __quantum__qis__h__body(q1);
1104        __quantum__qis__cnot__body(q1, q2);
1105        let r1 = __quantum__qis__m__body(q1);
1106        let r2 = __quantum__qis__m__body(q2);
1107        assert!(__quantum__rt__result_equal(r1, r2));
1108        __quantum__qis__dumpmachine__body(null_mut());
1109        __quantum__rt__qubit_release(q2);
1110        __quantum__rt__qubit_release(q1);
1111        let qs = __quantum__rt__qubit_allocate_array(4);
1112        unsafe {
1113            let q_elem = __quantum__rt__array_get_element_ptr_1d(qs, 3).cast::<*mut c_void>();
1114            __quantum__qis__x__body(*q_elem);
1115            __quantum__qis__dumpmachine__body(null_mut());
1116            let r = __quantum__qis__m__body(*q_elem);
1117            assert!(__quantum__rt__result_equal(
1118                r,
1119                __quantum__rt__result_get_one()
1120            ));
1121            __quantum__rt__qubit_release_array(qs);
1122        }
1123    }
1124
1125    #[test]
1126    fn test_qubit_is_zero() {
1127        let q0 = __quantum__rt__qubit_allocate();
1128        assert!(qubit_is_zero(q0));
1129        __quantum__qis__x__body(q0);
1130        assert!(!qubit_is_zero(q0));
1131        __quantum__qis__h__body(q0);
1132        assert!(!qubit_is_zero(q0));
1133        let r = __quantum__qis__m__body(q0);
1134        assert!(
1135            qubit_is_zero(q0) != __quantum__rt__result_equal(r, __quantum__rt__result_get_one())
1136        );
1137    }
1138
1139    #[allow(clippy::cast_ptr_alignment)]
1140    #[test]
1141    fn test_map_unmap_are_adjoint() {
1142        unsafe fn check_map_unmap(pauli: Pauli) {
1143            unsafe {
1144                let check_qubit = __quantum__rt__qubit_allocate();
1145                let qubits = __quantum__rt__qubit_allocate_array(1);
1146                let q = *__quantum__rt__array_get_element_ptr_1d(qubits, 0).cast::<*mut c_void>();
1147                let paulis = __quantum__rt__array_create_1d(1, 1);
1148                *__quantum__rt__array_get_element_ptr_1d(paulis, 0).cast::<Pauli>() = pauli;
1149
1150                __quantum__qis__h__body(check_qubit);
1151                __quantum__qis__cnot__body(check_qubit, q);
1152
1153                SIM_STATE.with(|sim_state| {
1154                    let state = &mut *sim_state.borrow_mut();
1155                    let combined_list = map_to_z_basis(state, paulis, qubits);
1156                    unmap_from_z_basis(state, combined_list);
1157                });
1158
1159                __quantum__qis__cnot__body(check_qubit, q);
1160                __quantum__qis__h__body(check_qubit);
1161
1162                assert!(qubit_is_zero(q));
1163                assert!(qubit_is_zero(check_qubit));
1164
1165                __quantum__rt__array_update_reference_count(paulis, -1);
1166                __quantum__rt__qubit_release_array(qubits);
1167                __quantum__rt__qubit_release(check_qubit);
1168            }
1169        }
1170
1171        unsafe {
1172            check_map_unmap(Pauli::X);
1173            check_map_unmap(Pauli::Y);
1174            check_map_unmap(Pauli::Z);
1175        }
1176    }
1177
1178    #[allow(clippy::cast_ptr_alignment)]
1179    #[test]
1180    fn test_map_pauli_x() {
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::X;
1186
1187            __quantum__qis__h__body(q);
1188
1189            SIM_STATE.with(|sim_state| {
1190                let state = &mut *sim_state.borrow_mut();
1191                let _ = map_to_z_basis(state, paulis, qubits);
1192            });
1193
1194            qubit_is_zero(q);
1195
1196            __quantum__rt__array_update_reference_count(paulis, -1);
1197            __quantum__rt__qubit_release_array(qubits);
1198        }
1199    }
1200
1201    #[allow(clippy::cast_ptr_alignment)]
1202    #[test]
1203    fn test_map_pauli_y() {
1204        let qubits = __quantum__rt__qubit_allocate_array(1);
1205        unsafe {
1206            let q = *__quantum__rt__array_get_element_ptr_1d(qubits, 0).cast::<*mut c_void>();
1207            let paulis = __quantum__rt__array_create_1d(1, 1);
1208            *__quantum__rt__array_get_element_ptr_1d(paulis, 0).cast::<Pauli>() = Pauli::Y;
1209
1210            __quantum__qis__h__body(q);
1211            __quantum__qis__s__adj(q);
1212            __quantum__qis__h__body(q);
1213
1214            SIM_STATE.with(|sim_state| {
1215                let state = &mut *sim_state.borrow_mut();
1216                let _ = map_to_z_basis(state, paulis, qubits);
1217            });
1218
1219            qubit_is_zero(q);
1220
1221            __quantum__rt__array_update_reference_count(paulis, -1);
1222            __quantum__rt__qubit_release_array(qubits);
1223        }
1224    }
1225
1226    #[allow(clippy::cast_ptr_alignment)]
1227    #[test]
1228    fn test_map_pauli_z() {
1229        let qubits = __quantum__rt__qubit_allocate_array(1);
1230        unsafe {
1231            let q = *__quantum__rt__array_get_element_ptr_1d(qubits, 0).cast::<*mut c_void>();
1232            let paulis = __quantum__rt__array_create_1d(1, 1);
1233            *__quantum__rt__array_get_element_ptr_1d(paulis, 0).cast::<Pauli>() = Pauli::Z;
1234
1235            SIM_STATE.with(|sim_state| {
1236                let state = &mut *sim_state.borrow_mut();
1237                let _ = map_to_z_basis(state, paulis, qubits);
1238            });
1239
1240            qubit_is_zero(q);
1241
1242            __quantum__rt__array_update_reference_count(paulis, -1);
1243            __quantum__rt__qubit_release_array(qubits);
1244        }
1245    }
1246
1247    #[test]
1248    fn test_joint_zz() {
1249        let check_qubit = __quantum__rt__qubit_allocate();
1250        let q0 = __quantum__rt__qubit_allocate();
1251        let q1 = __quantum__rt__qubit_allocate();
1252
1253        __quantum__qis__h__body(check_qubit);
1254        __quantum__qis__cx__body(check_qubit, q0);
1255        __quantum__qis__cx__body(check_qubit, q1);
1256
1257        __quantum__qis__rzz__body(PI / 2.0, q0, q1);
1258        __quantum__qis__rz__body(-PI / 2.0, q0);
1259        __quantum__qis__rz__body(-PI / 2.0, q1);
1260
1261        __quantum__qis__cz__body(q0, q1);
1262
1263        __quantum__qis__cx__body(check_qubit, q1);
1264        __quantum__qis__cx__body(check_qubit, q0);
1265        __quantum__qis__h__body(check_qubit);
1266
1267        assert!(qubit_is_zero(check_qubit));
1268        assert!(qubit_is_zero(q0));
1269        assert!(qubit_is_zero(q1));
1270    }
1271
1272    #[test]
1273    fn test_joint_yy() {
1274        let check_qubit = __quantum__rt__qubit_allocate();
1275        let q0 = __quantum__rt__qubit_allocate();
1276        let q1 = __quantum__rt__qubit_allocate();
1277
1278        __quantum__qis__h__body(check_qubit);
1279        __quantum__qis__cx__body(check_qubit, q0);
1280        __quantum__qis__cx__body(check_qubit, q1);
1281
1282        __quantum__qis__h__body(q0);
1283        __quantum__qis__s__adj(q0);
1284        __quantum__qis__h__body(q0);
1285        __quantum__qis__h__body(q1);
1286        __quantum__qis__s__adj(q1);
1287        __quantum__qis__h__body(q1);
1288
1289        __quantum__qis__ryy__body(PI / 2.0, q0, q1);
1290        __quantum__qis__ry__body(-PI / 2.0, q0);
1291        __quantum__qis__ry__body(-PI / 2.0, q1);
1292
1293        __quantum__qis__h__body(q1);
1294        __quantum__qis__s__body(q1);
1295        __quantum__qis__h__body(q1);
1296        __quantum__qis__h__body(q0);
1297        __quantum__qis__s__body(q0);
1298        __quantum__qis__h__body(q0);
1299
1300        __quantum__qis__cz__body(q0, q1);
1301
1302        __quantum__qis__cx__body(check_qubit, q1);
1303        __quantum__qis__cx__body(check_qubit, q0);
1304        __quantum__qis__h__body(check_qubit);
1305
1306        assert!(qubit_is_zero(check_qubit));
1307        assert!(qubit_is_zero(q0));
1308        assert!(qubit_is_zero(q1));
1309    }
1310
1311    #[test]
1312    fn test_joint_xx() {
1313        let check_qubit = __quantum__rt__qubit_allocate();
1314        let q0 = __quantum__rt__qubit_allocate();
1315        let q1 = __quantum__rt__qubit_allocate();
1316
1317        __quantum__qis__h__body(check_qubit);
1318        __quantum__qis__cx__body(check_qubit, q0);
1319        __quantum__qis__cx__body(check_qubit, q1);
1320
1321        __quantum__qis__h__body(q0);
1322        __quantum__qis__h__body(q1);
1323
1324        __quantum__qis__rxx__body(PI / 2.0, q0, q1);
1325        __quantum__qis__rx__body(-PI / 2.0, q0);
1326        __quantum__qis__rx__body(-PI / 2.0, q1);
1327
1328        __quantum__qis__h__body(q0);
1329        __quantum__qis__h__body(q1);
1330
1331        __quantum__qis__cz__body(q0, q1);
1332
1333        __quantum__qis__cx__body(check_qubit, q1);
1334        __quantum__qis__cx__body(check_qubit, q0);
1335        __quantum__qis__h__body(check_qubit);
1336
1337        assert!(qubit_is_zero(check_qubit));
1338        assert!(qubit_is_zero(q0));
1339        assert!(qubit_is_zero(q1));
1340    }
1341
1342    #[test]
1343    fn test_mresetz() {
1344        let qubit = __quantum__rt__qubit_allocate();
1345        let r0 = std::ptr::null_mut();
1346        let r1 = 1 as *mut c_void;
1347        assert!(qubit_is_zero(qubit));
1348        __quantum__qis__mresetz__body(qubit, r0);
1349        assert!(!__quantum__qis__read_result__body(r0));
1350        assert!(qubit_is_zero(qubit));
1351        __quantum__qis__x__body(qubit);
1352        __quantum__qis__mresetz__body(qubit, r1);
1353        assert!(__quantum__qis__read_result__body(r1));
1354        assert!(qubit_is_zero(qubit));
1355    }
1356
1357    #[test]
1358    fn test_capture_quantum_state() {
1359        let qubit = __quantum__rt__qubit_allocate();
1360        let (state, qubit_count) = capture_quantum_state();
1361        assert_eq!(qubit_count, 1);
1362        assert_eq!(state.len(), 1);
1363        assert_eq!(state[0].0, BigUint::from(0u32));
1364        __quantum__qis__x__body(qubit);
1365        let (state, qubit_count) = capture_quantum_state();
1366        assert_eq!(qubit_count, 1);
1367        assert_eq!(state.len(), 1);
1368        assert_eq!(state[0].0, BigUint::from(1u32));
1369        __quantum__qis__h__body(qubit);
1370        let qubit2 = __quantum__rt__qubit_allocate();
1371        let (state, qubit_count) = capture_quantum_state();
1372        assert_eq!(qubit_count, 2);
1373        assert_eq!(state.len(), 2);
1374        assert_eq!(state[0].1, -state[1].1);
1375        __quantum__qis__h__body(qubit);
1376        __quantum__qis__x__body(qubit);
1377        let (state, qubit_count) = capture_quantum_state();
1378        assert_eq!(qubit_count, 2);
1379        assert_eq!(state.len(), 1);
1380        assert_eq!(state[0].0, BigUint::from(0u32));
1381        __quantum__rt__qubit_release(qubit);
1382        __quantum__rt__qubit_release(qubit2);
1383        let (state, qubit_count) = capture_quantum_state();
1384        assert_eq!(qubit_count, 0);
1385        assert_eq!(state.len(), 1);
1386        assert_eq!(state[0].0, BigUint::from(0u32));
1387    }
1388}