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::{ensure_sufficient_qubits, SIM_STATE};
11use qir_stdlib::{
12    arrays::{QirArray, __quantum__rt__array_get_element_ptr_1d, __quantum__rt__array_get_size_1d},
13    tuples::{__quantum__rt__tuple_create, __quantum__rt__tuple_update_reference_count},
14    Pauli,
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#[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 = __quantum__rt__array_get_size_1d(paulis);
46        let paulis: Vec<SparsePauli> = (0..paulis_size)
47            .map(|index| {
48                map_pauli(*__quantum__rt__array_get_element_ptr_1d(paulis, index).cast::<Pauli>())
49            })
50            .collect();
51
52        let qubits_size = __quantum__rt__array_get_size_1d(qubits);
53        let targets: Vec<usize> = (0..qubits_size)
54            .map(|index| {
55                let qubit_id = *__quantum__rt__array_get_element_ptr_1d(qubits, index)
56                    .cast::<*mut c_void>() as usize;
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#[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    __quantum__qis__exp__body(paulis, -theta, qubits);
77}
78
79#[derive(Copy, Clone)]
80#[repr(C)]
81struct ExpArgs {
82    paulis: *const QirArray,
83    theta: c_double,
84    qubits: *const QirArray,
85}
86
87/// QIR API for applying an exponential of a multi-qubit rotation about the given Pauli axes with the given angle and qubits.
88/// # Safety
89///
90/// This function should only be called with arrays and tuples created by the QIR runtime library.
91#[allow(clippy::cast_ptr_alignment)]
92#[no_mangle]
93pub unsafe extern "C" fn __quantum__qis__exp__ctl(
94    ctls: *const QirArray,
95    arg_tuple: *mut *const Vec<u8>,
96) {
97    SIM_STATE.with(|sim_state| {
98        let state = &mut *sim_state.borrow_mut();
99        let args = *arg_tuple.cast::<ExpArgs>();
100
101        let ctls_size = __quantum__rt__array_get_size_1d(ctls);
102        let ctls: Vec<usize> = (0..ctls_size)
103            .map(|index| {
104                let qubit_id = *__quantum__rt__array_get_element_ptr_1d(ctls, index)
105                    .cast::<*mut c_void>() as usize;
106                ensure_sufficient_qubits(&mut state.sim, qubit_id, &mut state.max_qubit_id);
107                qubit_id
108            })
109            .collect();
110
111        let paulis_size = __quantum__rt__array_get_size_1d(args.paulis);
112        let paulis: Vec<SparsePauli> = (0..paulis_size)
113            .map(|index| {
114                map_pauli(
115                    *__quantum__rt__array_get_element_ptr_1d(args.paulis, index).cast::<Pauli>(),
116                )
117            })
118            .collect();
119
120        let qubits_size = __quantum__rt__array_get_size_1d(args.qubits);
121        let targets: Vec<usize> = (0..qubits_size)
122            .map(|index| {
123                let qubit_id = *__quantum__rt__array_get_element_ptr_1d(args.qubits, index)
124                    .cast::<*mut c_void>() 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, args.theta, &targets);
131    });
132}
133
134/// QIR API for applying an exponential of a multi-qubit rotation about the given Pauli axes with the given angle and qubits.
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#[no_mangle]
140pub unsafe extern "C" fn __quantum__qis__exp__ctladj(
141    ctls: *const QirArray,
142    arg_tuple: *mut *const Vec<u8>,
143) {
144    let args = *arg_tuple.cast::<ExpArgs>();
145    let new_args = ExpArgs {
146        paulis: args.paulis,
147        theta: -args.theta,
148        qubits: args.qubits,
149    };
150    let new_arg_tuple = __quantum__rt__tuple_create(size_of::<ExpArgs>() as u64);
151    *new_arg_tuple.cast::<ExpArgs>() = new_args;
152    __quantum__qis__exp__ctl(ctls, new_arg_tuple);
153    __quantum__rt__tuple_update_reference_count(new_arg_tuple, -1);
154}