quest_bind/
operators.rs

1use std::ffi::CString;
2
3use crate::{
4    error::catch_quest_exception,
5    ffi::{
6        self,
7        pauliOpType as PauliOpType,
8    },
9    Qcomplex,
10    Qreal,
11    QuestEnv,
12    QuestError,
13    Qureg,
14};
15
16#[derive(Debug)]
17pub struct PauliHamil(pub(crate) ffi::PauliHamil);
18
19impl PauliHamil {
20    /// Dynamically allocates a Hamiltonian
21    ///
22    /// The Hamiltonian is expressed as a real-weighted sum of products of
23    /// Pauli operators.
24    ///
25    /// # Examples
26    ///
27    /// ```rust
28    /// # use quest_bind::*;
29    /// let hamil = PauliHamil::try_new(2, 3).unwrap();
30    /// ```
31    ///
32    /// See [QuEST API] for more information.
33    ///
34    /// # Errors
35    ///
36    /// Returns [`QuestError::InvalidQuESTInputError`](crate::QuestError::InvalidQuESTInputError) on
37    /// failure. This is an exception thrown by `QuEST`.
38    ///
39    /// [QuEST API]: https://quest-kit.github.io/QuEST/modules.html
40    pub fn try_new(
41        num_qubits: i32,
42        num_sum_terms: i32,
43    ) -> Result<Self, QuestError> {
44        catch_quest_exception(|| {
45            Self(unsafe { ffi::createPauliHamil(num_qubits, num_sum_terms) })
46        })
47    }
48
49    /// Creates a [`PauliHamil`] instance
50    /// populated with the data in filename `fn_`.
51    ///
52    /// # Bugs
53    ///
54    /// This function calls its C equivalent which unfortunately behaves
55    /// erratically when the file specified is incorrectly formatted or
56    /// inaccessible, often leading to seg-faults.  Use at your own risk.
57    pub fn try_new_from_file(fn_: &str) -> Result<Self, QuestError> {
58        let filename = CString::new(fn_).map_err(QuestError::NulError)?;
59        catch_quest_exception(|| {
60            Self(unsafe { ffi::createPauliHamilFromFile((*filename).as_ptr()) })
61        })
62    }
63}
64
65impl Drop for PauliHamil {
66    fn drop(&mut self) {
67        catch_quest_exception(|| unsafe { ffi::destroyPauliHamil(self.0) })
68            .expect("dropping PauliHamil should always succeed");
69    }
70}
71
72#[derive(Debug)]
73pub struct DiagonalOp<'a> {
74    pub(crate) env: &'a QuestEnv,
75    pub(crate) op:  ffi::DiagonalOp,
76}
77
78impl<'a> DiagonalOp<'a> {
79    pub fn try_new(
80        num_qubits: i32,
81        env: &'a QuestEnv,
82    ) -> Result<Self, QuestError> {
83        Ok(Self {
84            env,
85            op: catch_quest_exception(|| unsafe {
86                ffi::createDiagonalOp(num_qubits, env.0)
87            })?,
88        })
89    }
90
91    pub fn try_new_from_file(
92        fn_: &str,
93        env: &'a QuestEnv,
94    ) -> Result<Self, QuestError> {
95        let filename = CString::new(fn_).map_err(QuestError::NulError)?;
96
97        Ok(Self {
98            env,
99            op: catch_quest_exception(|| unsafe {
100                ffi::createDiagonalOpFromPauliHamilFile(
101                    (*filename).as_ptr(),
102                    env.0,
103                )
104            })?,
105        })
106    }
107}
108
109impl<'a> Drop for DiagonalOp<'a> {
110    fn drop(&mut self) {
111        catch_quest_exception(|| unsafe {
112            ffi::destroyDiagonalOp(self.op, self.env.0);
113        })
114        .expect("dropping DiagonalOp should always succeed");
115    }
116}
117
118/// Initialize [`PauliHamil`](crate::PauliHamil) instance with the given term
119/// coefficients
120///
121/// # Examples
122///
123/// ```rust
124/// # use quest_bind::*;
125/// use quest_bind::PauliOpType::*;
126///
127/// let hamil = &mut PauliHamil::try_new(2, 2).unwrap();
128///
129/// init_pauli_hamil(
130///     hamil,
131///     &[0.5, -0.5],
132///     &[PAULI_X, PAULI_Y, PAULI_I, PAULI_I, PAULI_Z, PAULI_X],
133/// )
134/// .unwrap();
135/// ```
136///
137/// See [QuEST API] for more information.
138///
139/// [QuEST API]: https://quest-kit.github.io/QuEST/modules.html
140#[allow(clippy::needless_pass_by_ref_mut)]
141pub fn init_pauli_hamil(
142    hamil: &mut PauliHamil,
143    coeffs: &[Qreal],
144    codes: &[PauliOpType],
145) -> Result<(), QuestError> {
146    catch_quest_exception(|| unsafe {
147        ffi::initPauliHamil(hamil.0, coeffs.as_ptr(), codes.as_ptr());
148    })
149}
150
151/// Update the GPU memory with the current values in `op`.
152///
153/// # Examples
154///
155/// ```rust
156/// # use quest_bind::*;
157/// let env = &QuestEnv::new();
158/// let op = &mut DiagonalOp::try_new(1, env).unwrap();
159///
160/// sync_diagonal_op(op).unwrap();
161/// ```
162/// See [QuEST API] for more information.
163///
164/// [QuEST API]: https://quest-kit.github.io/QuEST/modules.html
165#[allow(clippy::needless_pass_by_ref_mut)]
166pub fn sync_diagonal_op(op: &mut DiagonalOp<'_>) -> Result<(), QuestError> {
167    catch_quest_exception(|| unsafe {
168        ffi::syncDiagonalOp(op.op);
169    })
170}
171
172/// Overwrites the entire `DiagonalOp` with the given elements.
173///
174/// # Examples
175///
176/// ```rust
177/// # use quest_bind::*;
178/// let env = &QuestEnv::new();
179/// let mut op = &mut DiagonalOp::try_new(2, env).unwrap();
180///
181/// let real = &[1., 2., 3., 4.];
182/// let imag = &[5., 6., 7., 8.];
183/// init_diagonal_op(op, real, imag);
184/// ```
185/// See [QuEST API] for more information.
186///
187/// # Panics
188///
189/// This function will panic, if either `real` or `imag`
190/// have length smaller than `2.pow(num_qubits)`.
191///
192/// [QuEST API]: https://quest-kit.github.io/QuEST/modules.html
193#[allow(clippy::cast_sign_loss)]
194#[allow(clippy::needless_pass_by_ref_mut)]
195pub fn init_diagonal_op(
196    op: &mut DiagonalOp<'_>,
197    real: &[Qreal],
198    imag: &[Qreal],
199) -> Result<(), QuestError> {
200    let len_required = 2usize.pow(op.op.numQubits as u32);
201    assert!(real.len() >= len_required);
202    assert!(imag.len() >= len_required);
203    catch_quest_exception(|| unsafe {
204        ffi::initDiagonalOp(op.op, real.as_ptr(), imag.as_ptr());
205    })
206}
207
208/// Populates the diagonal operator \p op to be equivalent to the given Pauli
209/// Hamiltonian
210///
211/// Assuming `hamil` contains only `PAULI_I` or `PAULI_Z` operators.
212///
213/// # Examples
214///
215/// ```rust
216/// # use quest_bind::*;
217/// use quest_bind::PauliOpType::*;
218///
219/// let hamil = &mut PauliHamil::try_new(2, 2).unwrap();
220/// init_pauli_hamil(
221///     hamil,
222///     &[0.5, -0.5],
223///     &[PAULI_I, PAULI_Z, PAULI_Z, PAULI_Z],
224/// )
225/// .unwrap();
226///
227/// let env = &QuestEnv::new();
228/// let mut op = &mut DiagonalOp::try_new(2, env).unwrap();
229///
230/// init_diagonal_op_from_pauli_hamil(op, hamil).unwrap();
231/// ```
232///
233/// See [QuEST API] for more information.
234///
235/// [QuEST API]: https://quest-kit.github.io/QuEST/modules.html
236#[allow(clippy::needless_pass_by_ref_mut)]
237pub fn init_diagonal_op_from_pauli_hamil(
238    op: &mut DiagonalOp<'_>,
239    hamil: &PauliHamil,
240) -> Result<(), QuestError> {
241    catch_quest_exception(|| unsafe {
242        ffi::initDiagonalOpFromPauliHamil(op.op, hamil.0);
243    })
244}
245
246/// Modifies a subset of elements of `DiagonalOp`.
247///
248/// Starting at index `start_ind`, and ending at index
249/// `start_ind +  num_elems`.
250///
251/// # Examples
252///
253/// ```rust
254/// # use quest_bind::*;
255/// let env = &QuestEnv::new();
256/// let op = &mut DiagonalOp::try_new(3, env).unwrap();
257///
258/// let num_elems = 4;
259/// let re = &[1., 2., 3., 4.];
260/// let im = &[1., 2., 3., 4.];
261/// set_diagonal_op_elems(op, 0, re, im, num_elems).unwrap();
262/// ```
263///
264/// # Panics
265///
266/// This function will panic if either
267/// `real.len() >= num_elems as usize`, or
268/// `imag.len() >= num_elems as usize`.
269///
270/// See [QuEST API] for more information.
271///
272/// [QuEST API]: https://quest-kit.github.io/QuEST/modules.html
273#[allow(clippy::cast_sign_loss)]
274#[allow(clippy::cast_possible_truncation)]
275#[allow(clippy::needless_pass_by_ref_mut)]
276pub fn set_diagonal_op_elems(
277    op: &mut DiagonalOp<'_>,
278    start_ind: i64,
279    real: &[Qreal],
280    imag: &[Qreal],
281    num_elems: i64,
282) -> Result<(), QuestError> {
283    assert!(real.len() >= num_elems as usize);
284    assert!(imag.len() >= num_elems as usize);
285
286    catch_quest_exception(|| unsafe {
287        ffi::setDiagonalOpElems(
288            op.op,
289            start_ind,
290            real.as_ptr(),
291            imag.as_ptr(),
292            num_elems,
293        );
294    })
295}
296
297/// Apply a diagonal operator to the entire `qureg`.
298///
299/// # Examples
300///
301/// ```rust
302/// # use quest_bind::*;
303/// let env = &QuestEnv::new();
304/// let qureg =
305///     &mut Qureg::try_new(2, &env).expect("cannot allocate memory for Qureg");
306/// let op = &mut DiagonalOp::try_new(2, env).unwrap();
307///
308/// init_diagonal_op(op, &[1., 2., 3., 4.], &[5., 6., 7., 8.]).unwrap();
309/// apply_diagonal_op(qureg, &op).unwrap();
310/// ```
311///
312/// See [QuEST API] for more information.
313///
314/// [QuEST API]: https://quest-kit.github.io/QuEST/modules.html
315#[allow(clippy::needless_pass_by_ref_mut)]
316pub fn apply_diagonal_op(
317    qureg: &mut Qureg<'_>,
318    op: &DiagonalOp<'_>,
319) -> Result<(), QuestError> {
320    catch_quest_exception(|| unsafe {
321        ffi::applyDiagonalOp(qureg.reg, op.op);
322    })
323}
324
325/// Computes the expected value of the diagonal operator `op`.
326///
327/// Since `op` is not necessarily Hermitian, the expected value may be a complex
328/// number.
329///
330/// # Examples
331///
332/// ```rust
333/// # use quest_bind::*;
334/// let env = &QuestEnv::new();
335/// let qureg =
336///     &mut Qureg::try_new(2, &env).expect("cannot allocate memory for Qureg");
337/// let op = &mut DiagonalOp::try_new(2, env).unwrap();
338///
339/// init_diagonal_op(op, &[1., 2., 3., 4.], &[5., 6., 7., 8.]).unwrap();
340///
341/// let expec_val = calc_expec_diagonal_op(qureg, op).unwrap();
342///
343/// assert!((expec_val.re - 1.).abs() < EPSILON);
344/// assert!((expec_val.im - 5.).abs() < EPSILON);
345/// ```
346///
347/// See [QuEST API] for more information.
348///
349/// [QuEST API]: https://quest-kit.github.io/QuEST/modules.html
350pub fn calc_expec_diagonal_op(
351    qureg: &Qureg<'_>,
352    op: &DiagonalOp<'_>,
353) -> Result<Qcomplex, QuestError> {
354    catch_quest_exception(|| unsafe {
355        ffi::calcExpecDiagonalOp(qureg.reg, op.op)
356    })
357    .map(Into::into)
358}