• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Qiskit / qiskit / 13898608421

17 Mar 2025 11:44AM CUT coverage: 88.078% (-1.5%) from 89.552%
13898608421

Pull #14033

github

web-flow
Bump pypa/cibuildwheel from 2.23.0 to 2.23.1 in the github_actions group

Bumps the github_actions group with 1 update: [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel).


Updates `pypa/cibuildwheel` from 2.23.0 to 2.23.1
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/v2.23.1/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.23.0...v2.23.1)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github_actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #14033: Bump pypa/cibuildwheel from 2.23.0 to 2.23.1 in the github_actions group

72722 of 82565 relevant lines covered (88.08%)

357061.76 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/crates/cext/src/sparse_observable.rs
1
// This code is part of Qiskit.
2
//
3
// (C) Copyright IBM 2024
4
//
5
// This code is licensed under the Apache License, Version 2.0. You may
6
// obtain a copy of this license in the LICENSE.txt file in the root directory
7
// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8
//
9
// Any modifications or derivative works of this code must retain this
10
// copyright notice, and modified files need to carry a notice indicating
11
// that they have been altered from the originals.
12

13
use std::ffi::{c_char, CString};
14

15
use crate::exit_codes::{CInputError, ExitCode};
16
use num_complex::Complex64;
17

18
use qiskit_accelerate::sparse_observable::{BitTerm, SparseObservable, SparseTermView};
19

20
#[cfg(feature = "python_binding")]
21
use pyo3::ffi::PyObject;
22
#[cfg(feature = "python_binding")]
23
use pyo3::{Py, Python};
24
#[cfg(feature = "python_binding")]
25
use qiskit_accelerate::sparse_observable::PySparseObservable;
26

27
/// @ingroup QkObsTerm
28
/// A term in a [SparseObservable].
29
///
30
/// This contains the coefficient (``coeff``), the number of qubits of the observable
31
/// (``num_qubits``) and pointers to the ``bit_terms`` and ``indices`` arrays, which have
32
/// length ``len``. It's the responsibility of the user that the data is coherent,
33
/// see also the below section on safety.
34
///
35
/// # Safety
36
///
37
/// * ``bit_terms`` must be a non-null, aligned pointer to ``len`` elements of type ``BitTerm``.
38
/// * ``indices`` must be a non-null, aligned pointer to ``len`` elements of type ``uint32_t``.
39
#[repr(C)]
40
pub struct CSparseTerm {
41
    coeff: Complex64,
42
    len: usize,
43
    bit_terms: *mut BitTerm,
44
    indices: *mut u32,
45
    num_qubits: u32,
46
}
47

48
impl TryFrom<&CSparseTerm> for SparseTermView<'_> {
49
    type Error = CInputError;
50

51
    fn try_from(value: &CSparseTerm) -> Result<Self, Self::Error> {
×
52
        check_ptr(value.bit_terms)?;
×
53
        check_ptr(value.indices)?;
×
54

55
        // SAFETY: At this point we know the pointers are non-null and aligned. We rely on C
56
        // that the arrays have the appropriate length, which is documented as requirement in the
57
        // CSparseTerm class.
58
        let bit_terms = unsafe { ::std::slice::from_raw_parts(value.bit_terms, value.len) };
×
59
        let indices = unsafe { ::std::slice::from_raw_parts(value.indices, value.len) };
×
60

×
61
        Ok(SparseTermView {
×
62
            num_qubits: value.num_qubits,
×
63
            coeff: value.coeff,
×
64
            bit_terms,
×
65
            indices,
×
66
        })
×
67
    }
×
68
}
69

70
/// Check the pointer is not null and is aligned.
71
fn check_ptr<T>(ptr: *const T) -> Result<(), CInputError> {
×
72
    if ptr.is_null() {
×
73
        return Err(CInputError::NullPointerError);
×
74
    };
×
75
    if !ptr.is_aligned() {
×
76
        return Err(CInputError::AlignmentError);
×
77
    };
×
78
    Ok(())
×
79
}
×
80

81
/// Casts a const pointer to a reference. Panics is the pointer is null or not aligned.
82
///
83
/// # Safety
84
///
85
/// This function requires ``ptr`` to be point to an initialized object of type ``T``.
86
/// While the resulting reference exists, the memory pointed to must not be mutated.
87
unsafe fn const_ptr_as_ref<'a, T>(ptr: *const T) -> &'a T {
×
88
    check_ptr(ptr).unwrap();
×
89
    let as_ref = unsafe { ptr.as_ref() };
×
90
    as_ref.unwrap() // we know the pointer is not null, hence we can safely unwrap
×
91
}
×
92

93
/// Casts a mut pointer to a mut reference. Panics is the pointer is null or not aligned.
94
///
95
/// # Safety
96
///
97
/// This function requires ``ptr`` to be point to an initialized object of type ``T``.
98
/// While the resulting reference exists, the memory pointed to must not be accessed otherwise.
99
unsafe fn mut_ptr_as_ref<'a, T>(ptr: *mut T) -> &'a mut T {
×
100
    check_ptr(ptr).unwrap();
×
101
    let as_mut_ref = unsafe { ptr.as_mut() };
×
102
    as_mut_ref.unwrap() // we know the pointer is not null, hence we can safely unwrap
×
103
}
×
104

105
/// @ingroup QkObs
106
/// Construct the zero observable (without any terms).
107
///
108
/// @param num_qubits The number of qubits the observable is defined on.
109
///
110
/// @return A pointer to the created observable.
111
///
112
/// # Example
113
///
114
///     QkObs *zero = qk_obs_zero(100);
115
///
116
#[no_mangle]
117
#[cfg(feature = "cbinding")]
118
pub extern "C" fn qk_obs_zero(num_qubits: u32) -> *mut SparseObservable {
×
119
    let obs = SparseObservable::zero(num_qubits);
×
120
    Box::into_raw(Box::new(obs))
×
121
}
×
122

123
/// @ingroup QkObs
124
/// Construct the identity observable.
125
///
126
/// @param num_qubits The number of qubits the observable is defined on.
127
///
128
/// @return A pointer to the created observable.
129
///
130
/// # Example
131
///
132
///     QkObs *identity = qk_obs_identity(100);
133
///
134
#[no_mangle]
135
#[cfg(feature = "cbinding")]
136
pub extern "C" fn qk_obs_identity(num_qubits: u32) -> *mut SparseObservable {
×
137
    let obs = SparseObservable::identity(num_qubits);
×
138
    Box::into_raw(Box::new(obs))
×
139
}
×
140

141
/// @ingroup QkObs
142
/// Construct a new observable from raw data.
143
///
144
/// @param num_qubits The number of qubits the observable is defined on.
145
/// @param num_terms The number of terms.
146
/// @param num_bits The total number of non-identity bit terms.
147
/// @param coeffs A pointer to the first element of the coefficients array, which has length
148
///     ``num_terms``.
149
/// @param bit_terms A pointer to the first element of the bit terms array, which has length
150
///     ``num_bits``.
151
/// @param indices A pointer to the first element of the indices array, which has length
152
///     ``num_bits``. Note that, per term, these *must* be sorted incrementally.
153
/// @param boundaries A pointer to the first element of the boundaries array, which has length
154
///     ``num_terms + 1``.
155
///
156
/// @return If the input data was coherent and the construction successful, the result is a pointer
157
///     to the observable. Otherwise a null pointer is returned.
158
///
159
/// # Example
160
///
161
///     // define the raw data for the 100-qubit observable |01><01|_{0, 1} - |+-><+-|_{98, 99}
162
///     uint32_t num_qubits = 100;
163
///     uint64_t num_terms = 2;  // we have 2 terms: |01><01|, -1 * |+-><+-|
164
///     uint64_t num_bits = 4; // we have 4 non-identity bits: 0, 1, +, -
165
///
166
///     complex double coeffs[2] = {1, -1};
167
///     QkBitTerm bits[4] = {QkBitTerm_Zero, QkBitTerm_One, QkBitTerm_Plus, QkBitTerm_Minus};
168
///     uint32_t indices[4] = {0, 1, 98, 99};  // <-- e.g. {1, 0, 99, 98} would be invalid
169
///     size_t boundaries[3] = {0, 2, 4};
170
///
171
///     QkObs *obs = qk_obs_new(
172
///         num_qubits, num_terms, num_bits, coeffs, bits, indices, boundaries
173
///     );
174
///
175
/// # Safety
176
///
177
/// Behavior is undefined if any of the following conditions are violated:
178
///
179
///   * ``coeffs`` is a pointer to a ``complex double`` array of length ``num_terms``
180
///   * ``bit_terms`` is a pointer to an array of valid ``QkBitTerm`` elements of length ``num_bits``
181
///   * ``indices`` is a pointer to a ``uint32_t`` array of length ``num_bits``, which is
182
///     term-wise sorted in strict ascending order, and every element is smaller than ``num_qubits``
183
///   * ``boundaries`` is a pointer to a ``size_t`` array of length ``num_terms + 1``, which is
184
///     sorted in ascending order, the first element is 0 and the last element is
185
///     smaller than ``num_terms``
186
#[no_mangle]
187
#[cfg(feature = "cbinding")]
188
pub unsafe extern "C" fn qk_obs_new(
×
189
    num_qubits: u32,
×
190
    num_terms: u64,
×
191
    num_bits: u64,
×
192
    coeffs: *mut Complex64,
×
193
    bit_terms: *mut BitTerm,
×
194
    indices: *mut u32,
×
195
    boundaries: *mut usize,
×
196
) -> *mut SparseObservable {
×
197
    let num_terms = num_terms as usize;
×
198
    let num_bits = num_bits as usize;
×
199

×
200
    check_ptr(coeffs).unwrap();
×
201
    check_ptr(bit_terms).unwrap();
×
202
    check_ptr(indices).unwrap();
×
203
    check_ptr(boundaries).unwrap();
×
204

×
205
    // SAFETY: At this point we know the pointers are non-null and aligned. We rely on C that
×
206
    // the pointers point to arrays of appropriate length, as specified in the function docs.
×
207
    let coeffs = unsafe { ::std::slice::from_raw_parts(coeffs, num_terms).to_vec() };
×
208
    let bit_terms = unsafe { ::std::slice::from_raw_parts(bit_terms, num_bits).to_vec() };
×
209
    let indices = unsafe { ::std::slice::from_raw_parts(indices, num_bits).to_vec() };
×
210
    let boundaries = unsafe { ::std::slice::from_raw_parts(boundaries, num_terms + 1).to_vec() };
×
211

×
212
    let result = SparseObservable::new(num_qubits, coeffs, bit_terms, indices, boundaries);
×
213
    match result {
×
214
        Ok(obs) => Box::into_raw(Box::new(obs)),
×
215
        Err(_) => ::std::ptr::null_mut(),
×
216
    }
217
}
×
218

219
/// @ingroup QkObs
220
/// Free the observable.
221
///
222
/// @param obs A pointer to the observable to free.
223
///
224
/// # Example
225
///
226
///     QkObs *obs = qk_obs_zero(100);
227
///     qk_obs_free(obs);
228
///
229
/// # Safety
230
///
231
/// Behavior is undefined if ``obs`` is not either null or a valid pointer to a
232
/// [SparseObservable].
233
#[no_mangle]
234
#[cfg(feature = "cbinding")]
235
pub unsafe extern "C" fn qk_obs_free(obs: *mut SparseObservable) {
×
236
    if !obs.is_null() {
×
237
        if !obs.is_aligned() {
×
238
            panic!("Attempted to free a non-aligned pointer.")
×
239
        }
×
240

×
241
        // SAFETY: We have verified the pointer is non-null and aligned, so it should be
×
242
        // readable by Box.
×
243
        unsafe {
×
244
            let _ = Box::from_raw(obs);
×
245
        }
×
246
    }
×
247
}
×
248

249
/// @ingroup QkObs
250
/// Add a term to the observable.
251
///
252
/// @param obs A pointer to the observable.
253
/// @param cterm A pointer to the term to add.
254
///
255
/// @return An exit code. This is ``>0`` if the term is incoherent or adding the term fails.
256
///
257
/// # Example
258
///
259
///     uint32_t num_qubits = 100;
260
///     QkObs *obs = qk_obs_zero(num_qubits);
261
///
262
///     complex double coeff = 1;
263
///     QkBitTerm bit_terms[3] = {QkBitTerm_X, QkBitTerm_Y, QkBitTerm_Z};
264
///     uint32_t indices[3] = {0, 1, 2};
265
///     QkObsTerm term = {&coeff, 3, bit_terms, indices, num_qubits};
266
///
267
///     int exit_code = qk_obs_add_term(obs, &term);
268
///
269
/// # Safety
270
///
271
/// Behavior is undefined if any of the following is violated:
272
///
273
///   * ``obs`` is a valid, non-null pointer to a ``QkObs``
274
///   * ``cterm`` is a valid, non-null pointer to a ``QkObsTerm``
275
#[no_mangle]
276
#[cfg(feature = "cbinding")]
277
pub unsafe extern "C" fn qk_obs_add_term(
×
278
    obs: *mut SparseObservable,
×
279
    cterm: *const CSparseTerm,
×
280
) -> ExitCode {
×
281
    // SAFETY: Per documentation, the pointers are non-null and aligned.
×
282
    let obs = unsafe { mut_ptr_as_ref(obs) };
×
283
    let cterm = unsafe { const_ptr_as_ref(cterm) };
×
284

285
    let view = match cterm.try_into() {
×
286
        Ok(view) => view,
×
287
        Err(err) => return ExitCode::from(err),
×
288
    };
289

290
    match obs.add_term(view) {
×
291
        Ok(_) => ExitCode::Success,
×
292
        Err(err) => ExitCode::from(err),
×
293
    }
294
}
×
295

296
/// @ingroup QkObs
297
/// Get an observable term by reference.
298
///
299
/// A [CSparseTerm] contains pointers to the indices and bit terms in the term, which
300
/// can be used to modify the internal data of the observable. This can leave the observable
301
/// in an incoherent state and should be avoided, unless great care is taken. It is generally
302
/// safer to construct a new observable instead of attempting in-place modifications.
303
///
304
/// @param obs A pointer to the observable.
305
/// @param index The index of the term to get.
306
/// @param out A pointer to a [CSparseTerm] used to return the observable term.
307
///
308
/// @return An exit code.
309
///
310
/// # Example
311
///
312
///     QkObs *obs = qk_obs_identity(100);
313
///     QkObsTerm term;
314
///     int exit_code = qk_obs_term(obs, 0, &term);
315
///     // out-of-bounds indices return an error code
316
///     // int error = qk_obs_term(obs, 12, &term);
317
///
318
/// # Safety
319
///
320
/// Behavior is undefined if any of the following is violated
321
/// * ``obs`` is a valid, non-null pointer to a ``QkObs``
322
/// * ``out`` is a valid, non-null pointer to a ``QkObsTerm``
323
#[no_mangle]
324
#[cfg(feature = "cbinding")]
325
pub unsafe extern "C" fn qk_obs_term(
×
326
    obs: *mut SparseObservable,
×
327
    index: u64,
×
328
    out: *mut CSparseTerm,
×
329
) -> ExitCode {
×
330
    // SAFETY: Per documentation, the pointers are non-null and aligned.
×
331
    let out = unsafe { mut_ptr_as_ref(out) };
×
332
    let obs = unsafe { mut_ptr_as_ref(obs) };
×
333

×
334
    let index = index as usize;
×
335
    if index > obs.num_terms() {
×
336
        return ExitCode::IndexError;
×
337
    }
×
338

×
339
    out.len = obs.boundaries()[index + 1] - obs.boundaries()[index];
×
340
    out.coeff = obs.coeffs()[index];
×
341
    out.num_qubits = obs.num_qubits();
×
342

×
343
    let start = obs.boundaries()[index];
×
344
    out.bit_terms = &mut obs.bit_terms_mut()[start];
×
345
    // SAFETY: mutating the indices can leave the observable in an incoherent state.
×
346
    out.indices = &mut unsafe { obs.indices_mut() }[start];
×
347

×
348
    ExitCode::Success
×
349
}
×
350

351
/// @ingroup QkObs
352
/// Get the number of terms in the observable.
353
///
354
/// @param obs A pointer to the observable.
355
///
356
/// @return The number of terms in the observable.
357
///
358
/// # Example
359
///
360
///     QkObs *obs = qk_obs_identity(100);
361
///     size_t num_terms = qk_obs_num_terms(obs);  // num_terms==1
362
///
363
/// # Safety
364
///
365
/// Behavior is undefined ``obs`` is not a valid, non-null pointer to a ``QkObs``.
366
#[no_mangle]
367
#[cfg(feature = "cbinding")]
368
pub unsafe extern "C" fn qk_obs_num_terms(obs: *const SparseObservable) -> usize {
×
369
    // SAFETY: Per documentation, the pointer is non-null and aligned.
×
370
    let obs = unsafe { const_ptr_as_ref(obs) };
×
371

×
372
    obs.num_terms()
×
373
}
×
374

375
/// @ingroup QkObs
376
/// Get the number of qubits the observable is defined on.
377
///
378
/// @param obs A pointer to the observable.
379
///
380
/// @return The number of qubits the observable is defined on.
381
///
382
/// # Example
383
///
384
///     QkObs *obs = qk_obs_identity(100);
385
///     uint32_t num_qubits = qk_obs_num_qubits(obs);  // num_qubits==100
386
///
387
/// # Safety
388
///
389
/// Behavior is undefined ``obs`` is not a valid, non-null pointer to a ``QkObs``.
390
#[no_mangle]
391
#[cfg(feature = "cbinding")]
392
pub unsafe extern "C" fn qk_obs_num_qubits(obs: *const SparseObservable) -> u32 {
×
393
    // SAFETY: Per documentation, the pointer is non-null and aligned.
×
394
    let obs = unsafe { const_ptr_as_ref(obs) };
×
395

×
396
    obs.num_qubits()
×
397
}
×
398

399
/// @ingroup QkObs
400
/// Get the number of bit terms/indices in the observable.
401
///
402
/// @param obs A pointer to the observable.
403
///
404
/// @return The number of terms in the observable.
405
///
406
/// # Example
407
///
408
///     QkObs *obs = qk_obs_identity(100);
409
///     size_t len = qk_obs_len(obs);  // len==0, as there are no non-trivial bit terms
410
///
411
/// # Safety
412
///
413
/// Behavior is undefined ``obs`` is not a valid, non-null pointer to a ``QkObs``.
414
#[no_mangle]
415
#[cfg(feature = "cbinding")]
416
pub unsafe extern "C" fn qk_obs_len(obs: *const SparseObservable) -> usize {
×
417
    // SAFETY: Per documentation, the pointer is non-null and aligned.
×
418
    let obs = unsafe { const_ptr_as_ref(obs) };
×
419

×
420
    obs.bit_terms().len()
×
421
}
×
422

423
/// @ingroup QkObs
424
/// Get a pointer to the coefficients.
425
///
426
/// This can be used to read and modify the observable's coefficients. The resulting
427
/// pointer is valid to read for ``qk_obs_num_terms(obs)`` elements of ``complex double``.
428
///
429
/// @param obs A pointer to the observable.
430
///
431
/// @return A pointer to the coefficients.
432
///
433
/// # Example
434
///
435
///     QkObs *obs = qk_obs_identity(100);
436
///     size_t num_terms = qk_obs_num_terms(obs);
437
///     complex double *coeffs = qk_obs_coeffs(obs);
438
///
439
///     for (size_t i = 0; i < num_terms; i++) {
440
///         printf("%f + i%f\n", creal(coeffs[i]), cimag(coeffs[i]));
441
///     }
442
///
443
/// # Safety
444
///
445
/// Behavior is undefined ``obs`` is not a valid, non-null pointer to a ``QkObs``.
446
#[no_mangle]
447
#[cfg(feature = "cbinding")]
448
pub unsafe extern "C" fn qk_obs_coeffs(obs: *mut SparseObservable) -> *mut Complex64 {
×
449
    // SAFETY: Per documentation, the pointer is non-null and aligned.
×
450
    let obs = unsafe { mut_ptr_as_ref(obs) };
×
451

×
452
    obs.coeffs_mut().as_mut_ptr()
×
453
}
×
454

455
/// @ingroup QkObs
456
/// Get a pointer to the indices.
457
///
458
/// This can be used to read and modify the observable's indices. The resulting pointer is
459
/// valid to read for ``qk_obs_len(obs)`` elements of size ``uint32_t``.
460
///
461
/// @param obs A pointer to the observable.
462
///
463
/// @return A pointer to the indices.
464
///
465
/// # Example
466
///
467
///     uint32_t num_qubits = 100;
468
///     QkObs *obs = qk_obs_zero(num_qubits);
469
///
470
///     complex double coeff = 1;
471
///     QkBitTerm bit_terms[3] = {QkBitTerm_X, QkBitTerm_Y, QkBitTerm_Z};
472
///     uint32_t indices[3] = {0, 1, 2};
473
///     QkObsTerm term = {&coeff, 3, bit_terms, indices, num_qubits};
474
///     qk_obs_add_term(obs, &term);
475
///
476
///     size_T len = qk_obs_len(obs);
477
///     uint32_t *indices = qk_obs_indices(obs);
478
///
479
///     for (size_t i = 0; i < len; i++) {
480
///         printf("index %i: %i\n", i, indices[i]);
481
///     }
482
///
483
/// # Safety
484
///
485
/// Behavior is undefined ``obs`` is not a valid, non-null pointer to a ``QkObs``.
486
#[no_mangle]
487
#[cfg(feature = "cbinding")]
488
pub unsafe extern "C" fn qk_obs_indices(obs: *mut SparseObservable) -> *mut u32 {
×
489
    // SAFETY: Per documentation, the pointer is non-null and aligned.
×
490
    let obs = unsafe { mut_ptr_as_ref(obs) };
×
491

×
492
    // SAFETY: Mutating the indices can leave the observable in an incoherent state.
×
493
    unsafe { obs.indices_mut() }.as_mut_ptr()
×
494
}
×
495

496
/// @ingroup QkObs
497
/// Get a pointer to the term boundaries.
498
///
499
/// This can be used to read and modify the observable's term boundaries. The resulting pointer is
500
/// valid to read for ``qk_obs_num_terms(obs) + 1`` elements of size ``size_t``.
501
///
502
/// @param obs A pointer to the observable.
503
///
504
/// @return A pointer to the boundaries.
505
///
506
/// # Example
507
///
508
///     uint32_t num_qubits = 100;
509
///     QkObs *obs = qk_obs_zero(num_qubits);
510
///
511
///     complex double coeff = 1;
512
///     QkBitTerm bit_terms[3] = {QkBitTerm_X, QkBitTerm_Y, QkBitTerm_Z};
513
///     uint32_t indices[3] = {0, 1, 2};
514
///     QkObsTerm term = {&coeff, 3, bit_terms, indices, num_qubits};
515
///     qk_obs_add_term(obs, &term);
516
///
517
///     size_t num_terms = qk_obs_num_terms(obs);
518
///     uint32_t *boundaries = qk_obs_boundaries(obs);
519
///
520
///     for (size_t i = 0; i < num_terms + 1; i++) {
521
///         printf("boundary %i: %i\n", i, boundaries[i]);
522
///     }
523
///
524
/// # Safety
525
///
526
/// Behavior is undefined ``obs`` is not a valid, non-null pointer to a ``QkObs``.
527
#[no_mangle]
528
#[cfg(feature = "cbinding")]
529
pub unsafe extern "C" fn qk_obs_boundaries(obs: *mut SparseObservable) -> *mut usize {
×
530
    // SAFETY: Per documentation, the pointer is non-null and aligned.
×
531
    let obs = unsafe { mut_ptr_as_ref(obs) };
×
532

×
533
    // SAFETY: Modifying the boundaries can leave the observable in an incoherent state. It is
×
534
    // the responsibility of the user that the data is coherent.
×
535
    unsafe { obs.boundaries_mut() }.as_mut_ptr()
×
536
}
×
537

538
/// @ingroup QkObs
539
/// Get a pointer to the bit terms.
540
///
541
/// This can be used to read and modify the observable's bit terms. The resulting pointer is
542
/// valid to read for ``qk_obs_len(obs)`` elements of size ``uint8_t``.
543
///
544
/// @param obs A pointer to the observable.
545
///
546
/// @return A pointer to the bit terms.
547
///
548
/// # Example
549
///
550
///     uint32_t num_qubits = 100;
551
///     QkObs *obs = qk_obs_zero(num_qubits);
552
///
553
///     complex double coeff = 1;
554
///     QkBitTerm bit_terms[3] = {QkBitTerm_X, QkBitTerm_Y, QkBitTerm_Z};
555
///     uint32_t indices[3] = {0, 1, 2};
556
///     QkObsTerm term = {&coeff, 3, bit_terms, indices, num_qubits};
557
///     qk_obs_add_term(obs, &term);
558
///
559
///     size_t len = qk_obs_len(obs);
560
///     QkBitTerm *bits = qk_obs_bit_terms(obs);
561
///
562
///     for (size_t i = 0; i < len; i++) {
563
///         printf("bit term %i: %i\n", i, bits[i]);
564
///     }
565
///
566
/// # Safety
567
///
568
/// Behavior is undefined ``obs`` is not a valid, non-null pointer to a ``QkObs``,
569
/// or if invalid valus are written into the resulting ``QkBitTerm`` pointer.
570
#[no_mangle]
571
#[cfg(feature = "cbinding")]
572
pub unsafe extern "C" fn qk_obs_bit_terms(obs: *mut SparseObservable) -> *mut BitTerm {
×
573
    // SAFETY: Per documentation, the pointer is non-null and aligned.
×
574
    let obs = unsafe { mut_ptr_as_ref(obs) };
×
575

×
576
    obs.bit_terms_mut().as_mut_ptr()
×
577
}
×
578

579
/// @ingroup QkObs
580
/// Multiply the observable by a complex coefficient.
581
///
582
/// @param obs A pointer to the observable.
583
/// @param coeff The coefficient to multiply the observable with.
584
///
585
/// # Example
586
///
587
///     QkObs *obs = qk_obs_identity(100);
588
///     complex double coeff = 2;
589
///     QkObs *result = qk_obs_multiply(obs, &coeff);
590
///
591
/// # Safety
592
///
593
/// Behavior is undefined if any of the following is violated
594
/// * ``obs`` is a valid, non-null pointer to a ``QkObs``
595
/// * ``coeff`` is a valid, non-null pointer to a ``complex double``
596
#[no_mangle]
597
#[cfg(feature = "cbinding")]
598
pub unsafe extern "C" fn qk_obs_multiply(
×
599
    obs: *const SparseObservable,
×
600
    coeff: *const Complex64,
×
601
) -> *mut SparseObservable {
×
602
    // SAFETY: Per documentation, the pointers are non-null and aligned.
×
603
    let obs = unsafe { const_ptr_as_ref(obs) };
×
604
    let coeff = unsafe { const_ptr_as_ref(coeff) };
×
605

×
606
    let result = obs * (*coeff);
×
607
    Box::into_raw(Box::new(result))
×
608
}
×
609

610
/// @ingroup QkObs
611
/// Add two observables.
612
///
613
/// @param left A pointer to the left observable.
614
/// @param right A pointer to the right observable.
615
///
616
/// @return A pointer to the result ``left + right``.
617
///
618
/// # Example
619
///
620
///     QkObs *left = qk_obs_identity(100);
621
///     QkObs *right = qk_obs_zero(100);
622
///     QkObs *result = qk_obs_add(left, right);
623
///
624
/// # Safety
625
///
626
/// Behavior is undefined if ``left`` or ``right`` are not valid, non-null pointers to
627
/// ``QkObs``\ s.
628
#[no_mangle]
629
#[cfg(feature = "cbinding")]
630
pub unsafe extern "C" fn qk_obs_add(
×
631
    left: *const SparseObservable,
×
632
    right: *const SparseObservable,
×
633
) -> *mut SparseObservable {
×
634
    // SAFETY: Per documentation, the pointers are non-null and aligned.
×
635
    let left = unsafe { const_ptr_as_ref(left) };
×
636
    let right = unsafe { const_ptr_as_ref(right) };
×
637

×
638
    let result = left + right;
×
639
    Box::into_raw(Box::new(result))
×
640
}
×
641

642
/// @ingroup QkObs
643
/// Compose (multiply) two observables.
644
///
645
/// @param first One observable.
646
/// @param second The other observable.
647
///
648
/// @return ``first.compose(second)`` which equals the observable ``result = second @ first``,
649
///     in terms of the matrix multiplication ``@``.
650
///
651
/// # Example
652
///
653
///     QkObs *first = qk_obs_zero(100);
654
///     QkObs *second = qk_obs_identity(100);
655
///     QkObs *result = qk_obs_compose(first, second);
656
///
657
/// # Safety
658
///
659
/// Behavior is undefined if ``first`` or ``second`` are not valid, non-null pointers to
660
/// ``QkObs``\ s.
661
#[no_mangle]
662
#[cfg(feature = "cbinding")]
663
pub unsafe extern "C" fn qk_obs_compose(
×
664
    first: *const SparseObservable,
×
665
    second: *const SparseObservable,
×
666
) -> *mut SparseObservable {
×
667
    // SAFETY: Per documentation, the pointers are non-null and aligned.
×
668
    let first = unsafe { const_ptr_as_ref(first) };
×
669
    let second = unsafe { const_ptr_as_ref(second) };
×
670

×
671
    let result = first.compose(second);
×
672
    Box::into_raw(Box::new(result))
×
673
}
×
674

675
/// @ingroup QkObs
676
/// Compose (multiply) two observables according to a custom qubit order.
677
///
678
/// Notably, this allows composing two observables of different size.
679
///
680
/// @param first One observable.
681
/// @param second The other observable. The number of qubits must match the length of ``qargs``.
682
/// @param qargs The qubit arguments specified which indices in ``first`` to associate with
683
///     the ones in ``second``.
684
///
685
/// @return ``first.compose(second)`` which equals the observable ``result = second @ first``,
686
///     in terms of the matrix multiplication ``@``.
687
///
688
/// # Example
689
///
690
///     QkObs *first = qk_obs_zero(100);
691
///     QkObs *second = qk_obs_identity(100);
692
///     QkObs *result = qk_obs_compose(first, second);
693
///
694
/// # Safety
695
///
696
/// To call this function safely
697
///
698
///   * ``first`` and ``second`` must be valid, non-null pointers to ``QkObs``\ s
699
///   * ``qargs`` must point to an array of ``uint32_t``, readable for ``qk_obs_num_qubits(second)``
700
///     elements (meaning the number of qubits in ``second``)
701
#[no_mangle]
702
#[cfg(feature = "cbinding")]
703
pub unsafe extern "C" fn qk_obs_compose_map(
×
704
    first: *const SparseObservable,
×
705
    second: *const SparseObservable,
×
706
    qargs: *const u32,
×
707
) -> *mut SparseObservable {
×
708
    // SAFETY: Per documentation, the pointers are non-null and aligned.
×
709
    let first = unsafe { const_ptr_as_ref(first) };
×
710
    let second = unsafe { const_ptr_as_ref(second) };
×
711

712
    let qargs = if qargs.is_null() {
×
713
        if second.num_qubits() != 0 {
×
714
            panic!("If qargs is null, then second must have 0 qubits.");
×
715
        }
×
716
        &[]
×
717
    } else {
718
        if !qargs.is_aligned() {
×
719
            panic!("qargs pointer is not aligned to u32");
×
720
        }
×
721
        // SAFETY: Per documentation, qargs is safe to read up to ``second.num_qubits()`` elements,
×
722
        // which is the maximal value of ``index`` here.
×
723
        unsafe { ::std::slice::from_raw_parts(qargs, second.num_qubits() as usize) }
×
724
    };
725

726
    let qargs_map = |index: u32| qargs[index as usize];
×
727

728
    let result = first.compose_map(second, qargs_map);
×
729
    Box::into_raw(Box::new(result))
×
730
}
×
731

732
/// @ingroup QkObs
733
/// Calculate the canonical representation of the observable.
734
///
735
/// @param obs A pointer to the observable.
736
/// @param tol The tolerance below which coefficients are considered to be zero.
737
///
738
/// @return The canonical representation of the observable.
739
///
740
/// # Example
741
///
742
///     QkObs *iden = qk_obs_identity(100);
743
///     QkObs *two = qk_obs_add(iden, iden);
744
///
745
///     double tol = 1e-6;
746
///     QkObs *canonical = qk_obs_canonicalize(two);
747
///
748
/// # Safety
749
///
750
/// Behavior is undefined ``obs`` is not a valid, non-null pointer to a ``QkObs``.
751
#[no_mangle]
752
#[cfg(feature = "cbinding")]
753
pub unsafe extern "C" fn qk_obs_canonicalize(
×
754
    obs: *const SparseObservable,
×
755
    tol: f64,
×
756
) -> *mut SparseObservable {
×
757
    // SAFETY: Per documentation, the pointer is non-null and aligned.
×
758
    let obs = unsafe { const_ptr_as_ref(obs) };
×
759

×
760
    let result = obs.canonicalize(tol);
×
761
    Box::into_raw(Box::new(result))
×
762
}
×
763

764
/// @ingroup QkObs
765
/// Copy the observable.
766
///
767
/// @param obs A pointer to the observable.
768
///
769
/// @return A pointer to a copy of the observable.
770
///
771
/// # Example
772
///
773
///     QkObs *original = qk_obs_identity(100);
774
///     QkObs *copied = qk_obs_copy(original);
775
///
776
/// # Safety
777
///
778
/// Behavior is undefined ``obs`` is not a valid, non-null pointer to a ``QkObs``.
779
#[no_mangle]
780
#[cfg(feature = "cbinding")]
781
pub unsafe extern "C" fn qk_obs_copy(obs: *const SparseObservable) -> *mut SparseObservable {
×
782
    // SAFETY: Per documentation, the pointer is non-null and aligned.
×
783
    let obs = unsafe { const_ptr_as_ref(obs) };
×
784

×
785
    let copied = obs.clone();
×
786
    Box::into_raw(Box::new(copied))
×
787
}
×
788

789
/// @ingroup QkObs
790
/// Compare two observables for equality.
791
///
792
/// Note that this does not compare mathematical equality, but data equality. This means
793
/// that two observables might represent the same observable but not compare as equal.
794
///
795
/// @param obs A pointer to one observable.
796
/// @param other A pointer to another observable.
797
///
798
/// @return ``true`` if the observables are equal, ``false`` otherwise.
799
///
800
/// # Example
801
///
802
///     QkObs *observable = qk_obs_identity(100);
803
///     QkObs *other = qk_obs_identity(100);
804
///     bool are_equal = qk_obs_equal(observable, other);
805
///
806
/// # Safety
807
///
808
/// Behavior is undefined if ``obs`` or ``other`` are not valid, non-null pointers to
809
/// ``QkObs``\ s.
810
#[no_mangle]
811
#[cfg(feature = "cbinding")]
812
pub unsafe extern "C" fn qk_obs_equal(
×
813
    obs: *const SparseObservable,
×
814
    other: *const SparseObservable,
×
815
) -> bool {
×
816
    // SAFETY: Per documentation, the pointers are non-null and aligned.
×
817
    let obs = unsafe { const_ptr_as_ref(obs) };
×
818
    let other = unsafe { const_ptr_as_ref(other) };
×
819

×
820
    obs.eq(other)
×
821
}
×
822

823
/// @ingroup QkObs
824
/// Return a string representation of a ``SparseObservable``.
825
///
826
/// @param obs A pointer to the ``SparseObservable`` to get the string for.
827
///
828
/// @return A pointer to a nul-terminated char array of the string representation for ``obs``
829
///
830
/// # Example
831
///
832
///     QkObs *obs = qk_obs_identity(100);
833
///     char *string = qk_obs_str(obs);
834
///     qk_str_free(string);
835
///
836
/// # Safety
837
///
838
/// Behavior is undefined ``obs`` is not a valid, non-null pointer to a ``QkObs``.
839
///
840
/// The string must not be freed with the normal C free, you must use ``qk_str_free`` to
841
/// free the memory consumed by the String. Not calling ``qk_str_free`` will lead to a
842
/// memory leak.
843
///
844
/// Do not change the length of the string after it's returned (by writing a nul byte somewhere
845
/// inside the string or removing the final one), although values can be mutated.
846
#[no_mangle]
847
#[cfg(feature = "cbinding")]
848
pub unsafe extern "C" fn qk_obs_str(obs: *const SparseObservable) -> *mut c_char {
×
849
    // SAFETY: Per documentation, the pointer is non-null and aligned.
×
850
    let obs = unsafe { const_ptr_as_ref(obs) };
×
851
    let string: String = format!("{:?}", obs);
×
852
    CString::new(string).unwrap().into_raw()
×
853
}
×
854

855
/// @ingroup QkObs
856
/// Free a string representation.
857
///
858
/// @param string A pointer to the returned string representation from ``qk_obs_str`` or
859
///     ``qk_obsterm_str``.
860
///
861
/// # Safety
862
///
863
/// Behavior is undefined if ``str`` is not a pointer returned by ``qk_obs_str`` or
864
/// ``qk_obsterm_str``.
865
#[no_mangle]
866
#[cfg(feature = "cbinding")]
867
pub unsafe extern "C" fn qk_str_free(string: *mut c_char) {
×
868
    unsafe {
×
869
        let _ = CString::from_raw(string);
×
870
    }
×
871
}
×
872

873
/// @ingroup QkObsTerm
874
/// Return a string representation of the sparse term.
875
///
876
/// @param term A pointer to the term.
877
///
878
/// @return The function exit code. This is ``>0`` if reading the term failed.
879
///
880
/// # Example
881
///
882
///     QkObs *obs = qk_obs_identity(100);
883
///     QkObsTerm term;
884
///     qk_obs_term(obs, 0, &term);
885
///     char *string = qk_obsterm_str(&term);
886
///     qk_str_free(string);
887
///
888
/// # Safety
889
///
890
/// Behavior is undefined ``term`` is not a valid, non-null pointer to a ``QkObsTerm``.
891
///
892
/// The string must not be freed with the normal C free, you must use ``qk_str_free`` to
893
/// free the memory consumed by the String. Not calling ``qk_str_free`` will lead to a
894
/// memory leak.
895
///
896
/// Do not change the length of the string after it's returned, although values can be mutated.
897
#[no_mangle]
898
#[cfg(feature = "cbinding")]
899
pub unsafe extern "C" fn qk_obsterm_str(term: *const CSparseTerm) -> *mut c_char {
×
900
    // SAFETY: Per documentation, the pointer is non-null and aligned.
×
901
    let term = unsafe { const_ptr_as_ref(term) };
×
902

×
903
    let view: SparseTermView = term.try_into().unwrap();
×
904
    let string: String = format!("{:?}", view);
×
905
    CString::new(string).unwrap().into_raw()
×
906
}
×
907

908
/// @ingroup QkBitTerm
909
/// Get the label for a bit term.
910
///
911
/// @param bit_term The bit term.
912
///
913
/// @return The label as ``uint8_t``, which can be cast to ``char`` to obtain the character.
914
///
915
/// # Example
916
///
917
///     QkBitTerm bit_term = QkBitTerm_Y;
918
///     // cast the uint8_t to char
919
///     char label = qk_bitterm_label(bit_term);
920
///
921
/// # Safety
922
///
923
/// The behavior is undefined if ``bit_term`` is not a valid ``uint8_t`` value of a [BitTerm].
924
#[no_mangle]
925
#[cfg(feature = "cbinding")]
926
pub extern "C" fn qk_bitterm_label(bit_term: BitTerm) -> u8 {
×
927
    // BitTerm is implemented as u8, which is calling convention compatible with C,
×
928
    // hence we can pass ``bit_term`` by value
×
929
    bit_term
×
930
        .py_label()
×
931
        .chars()
×
932
        .next()
×
933
        .expect("Label has exactly one character") as u8
×
934
}
×
935

936
/// @ingroup SparseObservable
937
/// Convert to a Python-space [PySparseObservable].
938
///
939
/// @param obs The C-space [SparseObservable] pointer.
940
///
941
/// @return A Python object representing the [PySparseObservable].
942
///
943
/// # Safety
944
///
945
/// Behavior is undefined if ``obs`` is not a valid, non-null pointer to a ``QkObs``.
946
///
947
/// It is assumed that the thread currently executing this function holds the
948
/// Python GIL this is required to create the Python object returned by this
949
/// function.
950
#[no_mangle]
951
#[cfg(feature = "python_binding")]
952
#[cfg(feature = "cbinding")]
953
pub unsafe extern "C" fn qk_obs_to_python(obs: *const SparseObservable) -> *mut PyObject {
×
954
    // SAFETY: Per documentation, the pointer is non-null and aligned.
×
955
    let obs = unsafe { const_ptr_as_ref(obs) };
×
956
    let py_obs: PySparseObservable = obs.clone().into();
×
957

×
958
    // SAFETY: the C caller is required to hold the GIL.
×
959
    unsafe {
×
960
        let py = Python::assume_gil_acquired();
×
961
        Py::new(py, py_obs)
×
962
            .expect("Unable to create a Python object")
×
963
            .into_ptr()
×
964
    }
×
965
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc