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::arrays::{
12    __quantum__rt__array_get_element_ptr_1d, __quantum__rt__array_get_size_1d, QirArray,
13};
14use quantum_sparse_sim::exp::Pauli as SparsePauli;
15use std::os::raw::{c_double, c_void};
16
17fn map_pauli(pauli: u8) -> SparsePauli {
18    match pauli & 3 {
19        0 => SparsePauli::I,
20        1 => SparsePauli::X,
21        2 => SparsePauli::Z,
22        3 => SparsePauli::Y,
23        _ => unreachable!(),
24    }
25}
26
27/// QIR API for applying an exponential of a multi-qubit rotation about the given Pauli axes with the given angle and qubits.
28/// # Safety
29///
30/// This function should only be called with arrays and tuples created by the QIR runtime library.
31#[allow(clippy::cast_ptr_alignment)]
32#[unsafe(no_mangle)]
33pub unsafe extern "C" fn __quantum__qis__exp__body(
34    paulis: *const QirArray,
35    theta: c_double,
36    qubits: *const QirArray,
37) {
38    SIM_STATE.with(|sim_state| {
39        let state = &mut *sim_state.borrow_mut();
40
41        let paulis_size = unsafe { __quantum__rt__array_get_size_1d(paulis) };
42        let paulis: Vec<SparsePauli> = (0..paulis_size)
43            .map(|index| {
44                map_pauli(unsafe {
45                    *__quantum__rt__array_get_element_ptr_1d(paulis, index).cast::<u8>()
46                })
47            })
48            .collect();
49
50        let qubits_size = unsafe { __quantum__rt__array_get_size_1d(qubits) };
51        let targets: Vec<usize> = (0..qubits_size)
52            .map(|index| {
53                let qubit_id = unsafe {
54                    *__quantum__rt__array_get_element_ptr_1d(qubits, index).cast::<*mut c_void>()
55                        as usize
56                };
57                ensure_sufficient_qubits(&mut state.sim, qubit_id, &mut state.max_qubit_id);
58                qubit_id
59            })
60            .collect();
61
62        state.sim.exp(&paulis, theta, &targets);
63    });
64}
65
66/// QIR API for applying an adjoint exponential of a multi-qubit rotation about the given Pauli axes with the given angle and qubits.
67/// # Safety
68///
69/// This function should only be called with arrays and tuples created by the QIR runtime library.
70#[unsafe(no_mangle)]
71pub unsafe extern "C" fn __quantum__qis__exp__adj(
72    paulis: *const QirArray,
73    theta: c_double,
74    qubits: *const QirArray,
75) {
76    unsafe {
77        __quantum__qis__exp__body(paulis, -theta, qubits);
78    }
79}
80
81#[derive(Copy, Clone)]
82#[repr(C)]
83struct ExpArgs {
84    paulis: *const QirArray,
85    theta: c_double,
86    qubits: *const QirArray,
87}
88
89#[allow(clippy::cast_ptr_alignment)]
90unsafe fn apply_controlled_exp(
91    paulis: *const QirArray,
92    theta: c_double,
93    ctls: *const QirArray,
94    qubits: *const QirArray,
95) {
96    SIM_STATE.with(|sim_state| {
97        let state = &mut *sim_state.borrow_mut();
98
99        let ctls_size = unsafe { __quantum__rt__array_get_size_1d(ctls) };
100        let ctls: Vec<usize> = (0..ctls_size)
101            .map(|index| {
102                let qubit_id = unsafe {
103                    *__quantum__rt__array_get_element_ptr_1d(ctls, index).cast::<*mut c_void>()
104                } as usize;
105                ensure_sufficient_qubits(&mut state.sim, qubit_id, &mut state.max_qubit_id);
106                qubit_id
107            })
108            .collect();
109
110        let paulis_size = unsafe { __quantum__rt__array_get_size_1d(paulis) };
111        let paulis: Vec<SparsePauli> = (0..paulis_size)
112            .map(|index| {
113                map_pauli(unsafe {
114                    *__quantum__rt__array_get_element_ptr_1d(paulis, index).cast::<u8>()
115                })
116            })
117            .collect();
118
119        let qubits_size = unsafe { __quantum__rt__array_get_size_1d(qubits) };
120        let targets: Vec<usize> = (0..qubits_size)
121            .map(|index| {
122                let qubit_id = unsafe {
123                    *__quantum__rt__array_get_element_ptr_1d(qubits, index).cast::<*mut c_void>()
124                } as usize;
125                ensure_sufficient_qubits(&mut state.sim, qubit_id, &mut state.max_qubit_id);
126                qubit_id
127            })
128            .collect();
129
130        state.sim.mcexp(&ctls, &paulis, theta, &targets);
131    });
132}
133
134/// QIR API for applying a controlled multi-qubit Pauli exponential from a runtime tuple.
135/// # Safety
136///
137/// This function should only be called with arrays and tuples created by the QIR runtime library.
138#[allow(clippy::cast_ptr_alignment)]
139#[unsafe(no_mangle)]
140pub unsafe extern "C" fn __quantum__qis__exp__ctl(
141    ctls: *const QirArray,
142    arg_tuple: *mut *const Vec<u8>,
143) {
144    let args = unsafe { *arg_tuple.cast::<ExpArgs>() };
145    unsafe { apply_controlled_exp(args.paulis, args.theta, ctls, args.qubits) };
146}
147
148/// QIR API for applying the adjoint controlled multi-qubit Pauli exponential from a runtime tuple.
149/// # Safety
150///
151/// This function should only be called with arrays and tuples created by the QIR runtime library.
152#[allow(clippy::cast_ptr_alignment)]
153#[unsafe(no_mangle)]
154pub unsafe extern "C" fn __quantum__qis__exp__ctladj(
155    ctls: *const QirArray,
156    arg_tuple: *mut *const Vec<u8>,
157) {
158    let args = unsafe { *arg_tuple.cast::<ExpArgs>() };
159    unsafe { apply_controlled_exp(args.paulis, -args.theta, ctls, args.qubits) };
160}
161
162/// QIR API for applying a controlled multi-qubit Pauli exponential with flat arguments.
163/// # Safety
164///
165/// This function should only be called with arrays created by the QIR runtime library.
166#[allow(non_snake_case)]
167pub unsafe extern "C" fn __quantum__qis__exp__ctl_flat(
168    paulis: *const QirArray,
169    theta: c_double,
170    ctls: *const QirArray,
171    qubits: *const QirArray,
172) {
173    unsafe { apply_controlled_exp(paulis, theta, ctls, qubits) };
174}
175
176/// QIR API for applying the adjoint controlled multi-qubit Pauli exponential with flat arguments.
177/// # Safety
178///
179/// This function should only be called with arrays created by the QIR runtime library.
180#[allow(non_snake_case)]
181pub unsafe extern "C" fn __quantum__qis__exp__ctladj_flat(
182    paulis: *const QirArray,
183    theta: c_double,
184    ctls: *const QirArray,
185    qubits: *const QirArray,
186) {
187    unsafe { apply_controlled_exp(paulis, -theta, ctls, qubits) };
188}