qir_backend/
exp.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4// This file contains the native support for the multi-qubit Exp rotation gate.
5// See https://learn.microsoft.com/en-us/qsharp/api/qsharp/microsoft.quantum.intrinsic.exp for details on the gate.
6// This is intentionally kept separate from the main simulator implementation as it is likely to be removed
7// in favor of having high level languages decompose into CNOT and single qubit rotations (see
8// https://github.com/microsoft/qsharp-runtime/issues/999 and https://github.com/microsoft/QuantumLibraries/issues/579).
9
10use crate::{SIM_STATE, ensure_sufficient_qubits};
11use qir_stdlib::{
12    Pauli,
13    arrays::{__quantum__rt__array_get_element_ptr_1d, __quantum__rt__array_get_size_1d, QirArray},
14    tuples::{__quantum__rt__tuple_create, __quantum__rt__tuple_update_reference_count},
15};
16use quantum_sparse_sim::exp::Pauli as SparsePauli;
17use std::{
18    mem::size_of,
19    os::raw::{c_double, c_void},
20};
21
22fn map_pauli(pauli: Pauli) -> SparsePauli {
23    match pauli {
24        Pauli::I => SparsePauli::I,
25        Pauli::X => SparsePauli::X,
26        Pauli::Z => SparsePauli::Z,
27        Pauli::Y => SparsePauli::Y,
28    }
29}
30
31/// QIR API for applying an exponential of a multi-qubit rotation about the given Pauli axes with the given angle and qubits.
32/// # Safety
33///
34/// This function should only be called with arrays and tuples created by the QIR runtime library.
35#[allow(clippy::cast_ptr_alignment)]
36#[unsafe(no_mangle)]
37pub unsafe extern "C" fn __quantum__qis__exp__body(
38    paulis: *const QirArray,
39    theta: c_double,
40    qubits: *const QirArray,
41) {
42    SIM_STATE.with(|sim_state| {
43        let state = &mut *sim_state.borrow_mut();
44
45        let paulis_size = unsafe { __quantum__rt__array_get_size_1d(paulis) };
46        let paulis: Vec<SparsePauli> = (0..paulis_size)
47            .map(|index| {
48                map_pauli(unsafe {
49                    *__quantum__rt__array_get_element_ptr_1d(paulis, index).cast::<Pauli>()
50                })
51            })
52            .collect();
53
54        let qubits_size = unsafe { __quantum__rt__array_get_size_1d(qubits) };
55        let targets: Vec<usize> = (0..qubits_size)
56            .map(|index| {
57                let qubit_id = unsafe {
58                    *__quantum__rt__array_get_element_ptr_1d(qubits, index).cast::<*mut c_void>()
59                        as usize
60                };
61                ensure_sufficient_qubits(&mut state.sim, qubit_id, &mut state.max_qubit_id);
62                qubit_id
63            })
64            .collect();
65
66        state.sim.exp(&paulis, theta, &targets);
67    });
68}
69
70/// QIR API for applying an adjoint exponential of a multi-qubit rotation about the given Pauli axes with the given angle and qubits.
71/// # Safety
72///
73/// This function should only be called with arrays and tuples created by the QIR runtime library.
74#[unsafe(no_mangle)]
75pub unsafe extern "C" fn __quantum__qis__exp__adj(
76    paulis: *const QirArray,
77    theta: c_double,
78    qubits: *const QirArray,
79) {
80    unsafe {
81        __quantum__qis__exp__body(paulis, -theta, qubits);
82    }
83}
84
85#[derive(Copy, Clone)]
86#[repr(C)]
87struct ExpArgs {
88    paulis: *const QirArray,
89    theta: c_double,
90    qubits: *const QirArray,
91}
92
93/// QIR API for applying an exponential of a multi-qubit rotation about the given Pauli axes with the given angle and qubits.
94/// # Safety
95///
96/// This function should only be called with arrays and tuples created by the QIR runtime library.
97#[allow(clippy::cast_ptr_alignment)]
98#[unsafe(no_mangle)]
99pub unsafe extern "C" fn __quantum__qis__exp__ctl(
100    ctls: *const QirArray,
101    arg_tuple: *mut *const Vec<u8>,
102) {
103    SIM_STATE.with(|sim_state| {
104        let state = &mut *sim_state.borrow_mut();
105        let args = unsafe { *arg_tuple.cast::<ExpArgs>() };
106
107        let ctls_size = unsafe { __quantum__rt__array_get_size_1d(ctls) };
108        let ctls: Vec<usize> = (0..ctls_size)
109            .map(|index| {
110                let qubit_id = unsafe {
111                    *__quantum__rt__array_get_element_ptr_1d(ctls, index).cast::<*mut c_void>()
112                } as usize;
113                ensure_sufficient_qubits(&mut state.sim, qubit_id, &mut state.max_qubit_id);
114                qubit_id
115            })
116            .collect();
117
118        let paulis_size = unsafe { __quantum__rt__array_get_size_1d(args.paulis) };
119        let paulis: Vec<SparsePauli> = (0..paulis_size)
120            .map(|index| {
121                map_pauli(unsafe {
122                    *__quantum__rt__array_get_element_ptr_1d(args.paulis, index).cast::<Pauli>()
123                })
124            })
125            .collect();
126
127        let qubits_size = unsafe { __quantum__rt__array_get_size_1d(args.qubits) };
128        let targets: Vec<usize> = (0..qubits_size)
129            .map(|index| {
130                let qubit_id = unsafe {
131                    *__quantum__rt__array_get_element_ptr_1d(args.qubits, index)
132                        .cast::<*mut c_void>()
133                } as usize;
134                ensure_sufficient_qubits(&mut state.sim, qubit_id, &mut state.max_qubit_id);
135                qubit_id
136            })
137            .collect();
138
139        state.sim.mcexp(&ctls, &paulis, args.theta, &targets);
140    });
141}
142
143/// QIR API for applying an exponential of a multi-qubit rotation about the given Pauli axes with the given angle and qubits.
144/// # Safety
145///
146/// This function should only be called with arrays and tuples created by the QIR runtime library.
147#[allow(clippy::cast_ptr_alignment)]
148#[unsafe(no_mangle)]
149pub unsafe extern "C" fn __quantum__qis__exp__ctladj(
150    ctls: *const QirArray,
151    arg_tuple: *mut *const Vec<u8>,
152) {
153    let args = unsafe { *arg_tuple.cast::<ExpArgs>() };
154    let new_args = ExpArgs {
155        paulis: args.paulis,
156        theta: -args.theta,
157        qubits: args.qubits,
158    };
159    let new_arg_tuple = __quantum__rt__tuple_create(size_of::<ExpArgs>() as u64);
160    unsafe {
161        *new_arg_tuple.cast::<ExpArgs>() = new_args;
162        __quantum__qis__exp__ctl(ctls, new_arg_tuple);
163        __quantum__rt__tuple_update_reference_count(new_arg_tuple, -1);
164    }
165}