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

Qiskit / qiskit / 14708454658

28 Apr 2025 01:02PM CUT coverage: 87.922% (+0.03%) from 87.895%
14708454658

Pull #14255

github

web-flow
Bump pypa/cibuildwheel from 2.23.2 to 2.23.3 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.2 to 2.23.3
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.23.2...v2.23.3)

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

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

74355 of 84569 relevant lines covered (87.92%)

433676.4 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 crate::pointers::{check_ptr, const_ptr_as_ref, mut_ptr_as_ref};
17
use num_complex::Complex64;
18

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

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

28
/// A term in a ``QkObs``.
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 ``QkBitTerm``.
38
/// * ``indices`` must be a non-null, aligned pointer to ``len`` elements of type ``uint32_t``.
39
#[repr(C)]
40
pub struct CSparseTerm {
41
    /// The coefficient of the observable term.
42
    coeff: Complex64,
43
    /// Length of the ``bit_terms`` and ``indices`` arrays.
44
    len: usize,
45
    /// A non-null, aligned pointer to ``len`` elements of type ``QkBitTerm``.
46
    bit_terms: *mut BitTerm,
47
    /// A non-null, aligned pointer to ``len`` elements of type ``uint32_t``.
48
    indices: *mut u32,
49
    /// The number of qubits the observable term is defined on.
50
    num_qubits: u32,
51
}
52

53
impl TryFrom<&CSparseTerm> for SparseTermView<'_> {
54
    type Error = CInputError;
55

56
    fn try_from(value: &CSparseTerm) -> Result<Self, Self::Error> {
×
57
        check_ptr(value.bit_terms)?;
×
58
        check_ptr(value.indices)?;
×
59

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

×
66
        Ok(SparseTermView {
×
67
            num_qubits: value.num_qubits,
×
68
            coeff: value.coeff,
×
69
            bit_terms,
×
70
            indices,
×
71
        })
×
72
    }
×
73
}
74

75
/// @ingroup QkObs
76
/// Construct the zero observable (without any terms).
77
///
78
/// @param num_qubits The number of qubits the observable is defined on.
79
///
80
/// @return A pointer to the created observable.
81
///
82
/// # Example
83
///
84
///     QkObs *zero = qk_obs_zero(100);
85
///
86
#[no_mangle]
87
#[cfg(feature = "cbinding")]
88
pub extern "C" fn qk_obs_zero(num_qubits: u32) -> *mut SparseObservable {
×
89
    let obs = SparseObservable::zero(num_qubits);
×
90
    Box::into_raw(Box::new(obs))
×
91
}
×
92

93
/// @ingroup QkObs
94
/// Construct the identity observable.
95
///
96
/// @param num_qubits The number of qubits the observable is defined on.
97
///
98
/// @return A pointer to the created observable.
99
///
100
/// # Example
101
///
102
///     QkObs *identity = qk_obs_identity(100);
103
///
104
#[no_mangle]
105
#[cfg(feature = "cbinding")]
106
pub extern "C" fn qk_obs_identity(num_qubits: u32) -> *mut SparseObservable {
×
107
    let obs = SparseObservable::identity(num_qubits);
×
108
    Box::into_raw(Box::new(obs))
×
109
}
×
110

111
/// @ingroup QkObs
112
/// Construct a new observable from raw data.
113
///
114
/// @param num_qubits The number of qubits the observable is defined on.
115
/// @param num_terms The number of terms.
116
/// @param num_bits The total number of non-identity bit terms.
117
/// @param coeffs A pointer to the first element of the coefficients array, which has length
118
///     ``num_terms``.
119
/// @param bit_terms A pointer to the first element of the bit terms array, which has length
120
///     ``num_bits``.
121
/// @param indices A pointer to the first element of the indices array, which has length
122
///     ``num_bits``. Note that, per term, these *must* be sorted incrementally.
123
/// @param boundaries A pointer to the first element of the boundaries array, which has length
124
///     ``num_terms + 1``.
125
///
126
/// @return If the input data was coherent and the construction successful, the result is a pointer
127
///     to the observable. Otherwise a null pointer is returned.
128
///
129
/// # Example
130
///
131
///     // define the raw data for the 100-qubit observable |01><01|_{0, 1} - |+-><+-|_{98, 99}
132
///     uint32_t num_qubits = 100;
133
///     uint64_t num_terms = 2;  // we have 2 terms: |01><01|, -1 * |+-><+-|
134
///     uint64_t num_bits = 4; // we have 4 non-identity bits: 0, 1, +, -
135
///
136
///     complex double coeffs[2] = {1, -1};
137
///     QkBitTerm bits[4] = {QkBitTerm_Zero, QkBitTerm_One, QkBitTerm_Plus, QkBitTerm_Minus};
138
///     uint32_t indices[4] = {0, 1, 98, 99};  // <-- e.g. {1, 0, 99, 98} would be invalid
139
///     size_t boundaries[3] = {0, 2, 4};
140
///
141
///     QkObs *obs = qk_obs_new(
142
///         num_qubits, num_terms, num_bits, coeffs, bits, indices, boundaries
143
///     );
144
///
145
/// # Safety
146
///
147
/// Behavior is undefined if any of the following conditions are violated:
148
///
149
///   * ``coeffs`` is a pointer to a ``complex double`` array of length ``num_terms``
150
///   * ``bit_terms`` is a pointer to an array of valid ``QkBitTerm`` elements of length ``num_bits``
151
///   * ``indices`` is a pointer to a ``uint32_t`` array of length ``num_bits``, which is
152
///     term-wise sorted in strict ascending order, and every element is smaller than ``num_qubits``
153
///   * ``boundaries`` is a pointer to a ``size_t`` array of length ``num_terms + 1``, which is
154
///     sorted in ascending order, the first element is 0 and the last element is
155
///     smaller than ``num_terms``
156
#[no_mangle]
157
#[cfg(feature = "cbinding")]
158
pub unsafe extern "C" fn qk_obs_new(
×
159
    num_qubits: u32,
×
160
    num_terms: u64,
×
161
    num_bits: u64,
×
162
    coeffs: *mut Complex64,
×
163
    bit_terms: *mut BitTerm,
×
164
    indices: *mut u32,
×
165
    boundaries: *mut usize,
×
166
) -> *mut SparseObservable {
×
167
    let num_terms = num_terms as usize;
×
168
    let num_bits = num_bits as usize;
×
169

×
170
    check_ptr(coeffs).unwrap();
×
171
    check_ptr(bit_terms).unwrap();
×
172
    check_ptr(indices).unwrap();
×
173
    check_ptr(boundaries).unwrap();
×
174

×
175
    // SAFETY: At this point we know the pointers are non-null and aligned. We rely on C that
×
176
    // the pointers point to arrays of appropriate length, as specified in the function docs.
×
177
    let coeffs = unsafe { ::std::slice::from_raw_parts(coeffs, num_terms).to_vec() };
×
178
    let bit_terms = unsafe { ::std::slice::from_raw_parts(bit_terms, num_bits).to_vec() };
×
179
    let indices = unsafe { ::std::slice::from_raw_parts(indices, num_bits).to_vec() };
×
180
    let boundaries = unsafe { ::std::slice::from_raw_parts(boundaries, num_terms + 1).to_vec() };
×
181

×
182
    let result = SparseObservable::new(num_qubits, coeffs, bit_terms, indices, boundaries);
×
183
    match result {
×
184
        Ok(obs) => Box::into_raw(Box::new(obs)),
×
185
        Err(_) => ::std::ptr::null_mut(),
×
186
    }
187
}
×
188

189
/// @ingroup QkObs
190
/// Free the observable.
191
///
192
/// @param obs A pointer to the observable to free.
193
///
194
/// # Example
195
///
196
///     QkObs *obs = qk_obs_zero(100);
197
///     qk_obs_free(obs);
198
///
199
/// # Safety
200
///
201
/// Behavior is undefined if ``obs`` is not either null or a valid pointer to a ``QkObs``.
202
#[no_mangle]
203
#[cfg(feature = "cbinding")]
204
pub unsafe extern "C" fn qk_obs_free(obs: *mut SparseObservable) {
×
205
    if !obs.is_null() {
×
206
        if !obs.is_aligned() {
×
207
            panic!("Attempted to free a non-aligned pointer.")
×
208
        }
×
209

×
210
        // SAFETY: We have verified the pointer is non-null and aligned, so it should be
×
211
        // readable by Box.
×
212
        unsafe {
×
213
            let _ = Box::from_raw(obs);
×
214
        }
×
215
    }
×
216
}
×
217

218
/// @ingroup QkObs
219
/// Add a term to the observable.
220
///
221
/// @param obs A pointer to the observable.
222
/// @param cterm A pointer to the term to add.
223
///
224
/// @return An exit code. This is ``>0`` if the term is incoherent or adding the term fails.
225
///
226
/// # Example
227
///
228
///     uint32_t num_qubits = 100;
229
///     QkObs *obs = qk_obs_zero(num_qubits);
230
///
231
///     complex double coeff = 1;
232
///     QkBitTerm bit_terms[3] = {QkBitTerm_X, QkBitTerm_Y, QkBitTerm_Z};
233
///     uint32_t indices[3] = {0, 1, 2};
234
///     QkObsTerm term = {&coeff, 3, bit_terms, indices, num_qubits};
235
///
236
///     int exit_code = qk_obs_add_term(obs, &term);
237
///
238
/// # Safety
239
///
240
/// Behavior is undefined if any of the following is violated:
241
///
242
///   * ``obs`` is a valid, non-null pointer to a ``QkObs``
243
///   * ``cterm`` is a valid, non-null pointer to a ``QkObsTerm``
244
#[no_mangle]
245
#[cfg(feature = "cbinding")]
246
pub unsafe extern "C" fn qk_obs_add_term(
×
247
    obs: *mut SparseObservable,
×
248
    cterm: *const CSparseTerm,
×
249
) -> ExitCode {
×
250
    // SAFETY: Per documentation, the pointers are non-null and aligned.
×
251
    let obs = unsafe { mut_ptr_as_ref(obs) };
×
252
    let cterm = unsafe { const_ptr_as_ref(cterm) };
×
253

254
    let view = match cterm.try_into() {
×
255
        Ok(view) => view,
×
256
        Err(err) => return ExitCode::from(err),
×
257
    };
258

259
    match obs.add_term(view) {
×
260
        Ok(_) => ExitCode::Success,
×
261
        Err(err) => ExitCode::from(err),
×
262
    }
263
}
×
264

265
/// @ingroup QkObs
266
/// Get an observable term by reference.
267
///
268
/// A ``QkObsTerm`` contains pointers to the indices and bit terms in the term, which
269
/// can be used to modify the internal data of the observable. This can leave the observable
270
/// in an incoherent state and should be avoided, unless great care is taken. It is generally
271
/// safer to construct a new observable instead of attempting in-place modifications.
272
///
273
/// @param obs A pointer to the observable.
274
/// @param index The index of the term to get.
275
/// @param out A pointer to a ``QkObsTerm`` used to return the observable term.
276
///
277
/// @return An exit code.
278
///
279
/// # Example
280
///
281
///     QkObs *obs = qk_obs_identity(100);
282
///     QkObsTerm term;
283
///     int exit_code = qk_obs_term(obs, 0, &term);
284
///     // out-of-bounds indices return an error code
285
///     // int error = qk_obs_term(obs, 12, &term);
286
///
287
/// # Safety
288
///
289
/// Behavior is undefined if any of the following is violated
290
/// * ``obs`` is a valid, non-null pointer to a ``QkObs``
291
/// * ``out`` is a valid, non-null pointer to a ``QkObsTerm``
292
#[no_mangle]
293
#[cfg(feature = "cbinding")]
294
pub unsafe extern "C" fn qk_obs_term(
×
295
    obs: *mut SparseObservable,
×
296
    index: u64,
×
297
    out: *mut CSparseTerm,
×
298
) -> ExitCode {
×
299
    // SAFETY: Per documentation, the pointers are non-null and aligned.
×
300
    let out = unsafe { mut_ptr_as_ref(out) };
×
301
    let obs = unsafe { mut_ptr_as_ref(obs) };
×
302

×
303
    let index = index as usize;
×
304
    if index > obs.num_terms() {
×
305
        return ExitCode::IndexError;
×
306
    }
×
307

×
308
    out.len = obs.boundaries()[index + 1] - obs.boundaries()[index];
×
309
    out.coeff = obs.coeffs()[index];
×
310
    out.num_qubits = obs.num_qubits();
×
311

×
312
    let start = obs.boundaries()[index];
×
313
    out.bit_terms = &mut obs.bit_terms_mut()[start];
×
314
    // SAFETY: mutating the indices can leave the observable in an incoherent state.
×
315
    out.indices = &mut unsafe { obs.indices_mut() }[start];
×
316

×
317
    ExitCode::Success
×
318
}
×
319

320
/// @ingroup QkObs
321
/// Get the number of terms in the observable.
322
///
323
/// @param obs A pointer to the observable.
324
///
325
/// @return The number of terms in the observable.
326
///
327
/// # Example
328
///
329
///     QkObs *obs = qk_obs_identity(100);
330
///     size_t num_terms = qk_obs_num_terms(obs);  // num_terms==1
331
///
332
/// # Safety
333
///
334
/// Behavior is undefined ``obs`` is not a valid, non-null pointer to a ``QkObs``.
335
#[no_mangle]
336
#[cfg(feature = "cbinding")]
337
pub unsafe extern "C" fn qk_obs_num_terms(obs: *const SparseObservable) -> usize {
×
338
    // SAFETY: Per documentation, the pointer is non-null and aligned.
×
339
    let obs = unsafe { const_ptr_as_ref(obs) };
×
340

×
341
    obs.num_terms()
×
342
}
×
343

344
/// @ingroup QkObs
345
/// Get the number of qubits the observable is defined on.
346
///
347
/// @param obs A pointer to the observable.
348
///
349
/// @return The number of qubits the observable is defined on.
350
///
351
/// # Example
352
///
353
///     QkObs *obs = qk_obs_identity(100);
354
///     uint32_t num_qubits = qk_obs_num_qubits(obs);  // num_qubits==100
355
///
356
/// # Safety
357
///
358
/// Behavior is undefined ``obs`` is not a valid, non-null pointer to a ``QkObs``.
359
#[no_mangle]
360
#[cfg(feature = "cbinding")]
361
pub unsafe extern "C" fn qk_obs_num_qubits(obs: *const SparseObservable) -> u32 {
×
362
    // SAFETY: Per documentation, the pointer is non-null and aligned.
×
363
    let obs = unsafe { const_ptr_as_ref(obs) };
×
364

×
365
    obs.num_qubits()
×
366
}
×
367

368
/// @ingroup QkObs
369
/// Get the number of bit terms/indices in the observable.
370
///
371
/// @param obs A pointer to the observable.
372
///
373
/// @return The number of terms in the observable.
374
///
375
/// # Example
376
///
377
///     QkObs *obs = qk_obs_identity(100);
378
///     size_t len = qk_obs_len(obs);  // len==0, as there are no non-trivial bit terms
379
///
380
/// # Safety
381
///
382
/// Behavior is undefined ``obs`` is not a valid, non-null pointer to a ``QkObs``.
383
#[no_mangle]
384
#[cfg(feature = "cbinding")]
385
pub unsafe extern "C" fn qk_obs_len(obs: *const SparseObservable) -> usize {
×
386
    // SAFETY: Per documentation, the pointer is non-null and aligned.
×
387
    let obs = unsafe { const_ptr_as_ref(obs) };
×
388

×
389
    obs.bit_terms().len()
×
390
}
×
391

392
/// @ingroup QkObs
393
/// Get a pointer to the coefficients.
394
///
395
/// This can be used to read and modify the observable's coefficients. The resulting
396
/// pointer is valid to read for ``qk_obs_num_terms(obs)`` elements of ``complex double``.
397
///
398
/// @param obs A pointer to the observable.
399
///
400
/// @return A pointer to the coefficients.
401
///
402
/// # Example
403
///
404
///     QkObs *obs = qk_obs_identity(100);
405
///     size_t num_terms = qk_obs_num_terms(obs);
406
///     complex double *coeffs = qk_obs_coeffs(obs);
407
///
408
///     for (size_t i = 0; i < num_terms; i++) {
409
///         printf("%f + i%f\n", creal(coeffs[i]), cimag(coeffs[i]));
410
///     }
411
///
412
/// # Safety
413
///
414
/// Behavior is undefined ``obs`` is not a valid, non-null pointer to a ``QkObs``.
415
#[no_mangle]
416
#[cfg(feature = "cbinding")]
417
pub unsafe extern "C" fn qk_obs_coeffs(obs: *mut SparseObservable) -> *mut Complex64 {
×
418
    // SAFETY: Per documentation, the pointer is non-null and aligned.
×
419
    let obs = unsafe { mut_ptr_as_ref(obs) };
×
420

×
421
    obs.coeffs_mut().as_mut_ptr()
×
422
}
×
423

424
/// @ingroup QkObs
425
/// Get a pointer to the indices.
426
///
427
/// This can be used to read and modify the observable's indices. The resulting pointer is
428
/// valid to read for ``qk_obs_len(obs)`` elements of size ``uint32_t``.
429
///
430
/// @param obs A pointer to the observable.
431
///
432
/// @return A pointer to the indices.
433
///
434
/// # Example
435
///
436
///     uint32_t num_qubits = 100;
437
///     QkObs *obs = qk_obs_zero(num_qubits);
438
///
439
///     complex double coeff = 1;
440
///     QkBitTerm bit_terms[3] = {QkBitTerm_X, QkBitTerm_Y, QkBitTerm_Z};
441
///     uint32_t indices[3] = {0, 1, 2};
442
///     QkObsTerm term = {&coeff, 3, bit_terms, indices, num_qubits};
443
///     qk_obs_add_term(obs, &term);
444
///
445
///     size_T len = qk_obs_len(obs);
446
///     uint32_t *indices = qk_obs_indices(obs);
447
///
448
///     for (size_t i = 0; i < len; i++) {
449
///         printf("index %i: %i\n", i, indices[i]);
450
///     }
451
///
452
/// # Safety
453
///
454
/// Behavior is undefined ``obs`` is not a valid, non-null pointer to a ``QkObs``.
455
#[no_mangle]
456
#[cfg(feature = "cbinding")]
457
pub unsafe extern "C" fn qk_obs_indices(obs: *mut SparseObservable) -> *mut u32 {
×
458
    // SAFETY: Per documentation, the pointer is non-null and aligned.
×
459
    let obs = unsafe { mut_ptr_as_ref(obs) };
×
460

×
461
    // SAFETY: Mutating the indices can leave the observable in an incoherent state.
×
462
    unsafe { obs.indices_mut() }.as_mut_ptr()
×
463
}
×
464

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

×
502
    // SAFETY: Modifying the boundaries can leave the observable in an incoherent state. It is
×
503
    // the responsibility of the user that the data is coherent.
×
504
    unsafe { obs.boundaries_mut() }.as_mut_ptr()
×
505
}
×
506

507
/// @ingroup QkObs
508
/// Get a pointer to the bit terms.
509
///
510
/// This can be used to read and modify the observable's bit terms. The resulting pointer is
511
/// valid to read for ``qk_obs_len(obs)`` elements of size ``uint8_t``.
512
///
513
/// @param obs A pointer to the observable.
514
///
515
/// @return A pointer to the bit terms.
516
///
517
/// # Example
518
///
519
///     uint32_t num_qubits = 100;
520
///     QkObs *obs = qk_obs_zero(num_qubits);
521
///
522
///     complex double coeff = 1;
523
///     QkBitTerm bit_terms[3] = {QkBitTerm_X, QkBitTerm_Y, QkBitTerm_Z};
524
///     uint32_t indices[3] = {0, 1, 2};
525
///     QkObsTerm term = {&coeff, 3, bit_terms, indices, num_qubits};
526
///     qk_obs_add_term(obs, &term);
527
///
528
///     size_t len = qk_obs_len(obs);
529
///     QkBitTerm *bits = qk_obs_bit_terms(obs);
530
///
531
///     for (size_t i = 0; i < len; i++) {
532
///         printf("bit term %i: %i\n", i, bits[i]);
533
///     }
534
///
535
/// # Safety
536
///
537
/// Behavior is undefined ``obs`` is not a valid, non-null pointer to a ``QkObs``,
538
/// or if invalid valus are written into the resulting ``QkBitTerm`` pointer.
539
#[no_mangle]
540
#[cfg(feature = "cbinding")]
541
pub unsafe extern "C" fn qk_obs_bit_terms(obs: *mut SparseObservable) -> *mut BitTerm {
×
542
    // SAFETY: Per documentation, the pointer is non-null and aligned.
×
543
    let obs = unsafe { mut_ptr_as_ref(obs) };
×
544

×
545
    obs.bit_terms_mut().as_mut_ptr()
×
546
}
×
547

548
/// @ingroup QkObs
549
/// Multiply the observable by a complex coefficient.
550
///
551
/// @param obs A pointer to the observable.
552
/// @param coeff The coefficient to multiply the observable with.
553
///
554
/// # Example
555
///
556
///     QkObs *obs = qk_obs_identity(100);
557
///     complex double coeff = 2;
558
///     QkObs *result = qk_obs_multiply(obs, &coeff);
559
///
560
/// # Safety
561
///
562
/// Behavior is undefined if any of the following is violated
563
/// * ``obs`` is a valid, non-null pointer to a ``QkObs``
564
/// * ``coeff`` is a valid, non-null pointer to a ``complex double``
565
#[no_mangle]
566
#[cfg(feature = "cbinding")]
567
pub unsafe extern "C" fn qk_obs_multiply(
×
568
    obs: *const SparseObservable,
×
569
    coeff: *const Complex64,
×
570
) -> *mut SparseObservable {
×
571
    // SAFETY: Per documentation, the pointers are non-null and aligned.
×
572
    let obs = unsafe { const_ptr_as_ref(obs) };
×
573
    let coeff = unsafe { const_ptr_as_ref(coeff) };
×
574

×
575
    let result = obs * (*coeff);
×
576
    Box::into_raw(Box::new(result))
×
577
}
×
578

579
/// @ingroup QkObs
580
/// Add two observables.
581
///
582
/// @param left A pointer to the left observable.
583
/// @param right A pointer to the right observable.
584
///
585
/// @return A pointer to the result ``left + right``.
586
///
587
/// # Example
588
///
589
///     QkObs *left = qk_obs_identity(100);
590
///     QkObs *right = qk_obs_zero(100);
591
///     QkObs *result = qk_obs_add(left, right);
592
///
593
/// # Safety
594
///
595
/// Behavior is undefined if ``left`` or ``right`` are not valid, non-null pointers to
596
/// ``QkObs``\ s.
597
#[no_mangle]
598
#[cfg(feature = "cbinding")]
599
pub unsafe extern "C" fn qk_obs_add(
×
600
    left: *const SparseObservable,
×
601
    right: *const SparseObservable,
×
602
) -> *mut SparseObservable {
×
603
    // SAFETY: Per documentation, the pointers are non-null and aligned.
×
604
    let left = unsafe { const_ptr_as_ref(left) };
×
605
    let right = unsafe { const_ptr_as_ref(right) };
×
606

×
607
    let result = left + right;
×
608
    Box::into_raw(Box::new(result))
×
609
}
×
610

611
/// @ingroup QkObs
612
/// Compose (multiply) two observables.
613
///
614
/// @param first One observable.
615
/// @param second The other observable.
616
///
617
/// @return ``first.compose(second)`` which equals the observable ``result = second @ first``,
618
///     in terms of the matrix multiplication ``@``.
619
///
620
/// # Example
621
///
622
///     QkObs *first = qk_obs_zero(100);
623
///     QkObs *second = qk_obs_identity(100);
624
///     QkObs *result = qk_obs_compose(first, second);
625
///
626
/// # Safety
627
///
628
/// Behavior is undefined if ``first`` or ``second`` are not valid, non-null pointers to
629
/// ``QkObs``\ s.
630
#[no_mangle]
631
#[cfg(feature = "cbinding")]
632
pub unsafe extern "C" fn qk_obs_compose(
×
633
    first: *const SparseObservable,
×
634
    second: *const SparseObservable,
×
635
) -> *mut SparseObservable {
×
636
    // SAFETY: Per documentation, the pointers are non-null and aligned.
×
637
    let first = unsafe { const_ptr_as_ref(first) };
×
638
    let second = unsafe { const_ptr_as_ref(second) };
×
639

×
640
    let result = first.compose(second);
×
641
    Box::into_raw(Box::new(result))
×
642
}
×
643

644
/// @ingroup QkObs
645
/// Compose (multiply) two observables according to a custom qubit order.
646
///
647
/// Notably, this allows composing two observables of different size.
648
///
649
/// @param first One observable.
650
/// @param second The other observable. The number of qubits must match the length of ``qargs``.
651
/// @param qargs The qubit arguments specified which indices in ``first`` to associate with
652
///     the ones in ``second``.
653
///
654
/// @return ``first.compose(second)`` which equals the observable ``result = second @ first``,
655
///     in terms of the matrix multiplication ``@``.
656
///
657
/// # Example
658
///
659
///     QkObs *first = qk_obs_zero(100);
660
///     QkObs *second = qk_obs_identity(100);
661
///     QkObs *result = qk_obs_compose(first, second);
662
///
663
/// # Safety
664
///
665
/// To call this function safely
666
///
667
///   * ``first`` and ``second`` must be valid, non-null pointers to ``QkObs``\ s
668
///   * ``qargs`` must point to an array of ``uint32_t``, readable for ``qk_obs_num_qubits(second)``
669
///     elements (meaning the number of qubits in ``second``)
670
#[no_mangle]
671
#[cfg(feature = "cbinding")]
672
pub unsafe extern "C" fn qk_obs_compose_map(
×
673
    first: *const SparseObservable,
×
674
    second: *const SparseObservable,
×
675
    qargs: *const u32,
×
676
) -> *mut SparseObservable {
×
677
    // SAFETY: Per documentation, the pointers are non-null and aligned.
×
678
    let first = unsafe { const_ptr_as_ref(first) };
×
679
    let second = unsafe { const_ptr_as_ref(second) };
×
680

681
    let qargs = if qargs.is_null() {
×
682
        if second.num_qubits() != 0 {
×
683
            panic!("If qargs is null, then second must have 0 qubits.");
×
684
        }
×
685
        &[]
×
686
    } else {
687
        if !qargs.is_aligned() {
×
688
            panic!("qargs pointer is not aligned to u32");
×
689
        }
×
690
        // SAFETY: Per documentation, qargs is safe to read up to ``second.num_qubits()`` elements,
×
691
        // which is the maximal value of ``index`` here.
×
692
        unsafe { ::std::slice::from_raw_parts(qargs, second.num_qubits() as usize) }
×
693
    };
694

695
    let qargs_map = |index: u32| qargs[index as usize];
×
696

697
    let result = first.compose_map(second, qargs_map);
×
698
    Box::into_raw(Box::new(result))
×
699
}
×
700

701
/// @ingroup QkObs
702
/// Calculate the canonical representation of the observable.
703
///
704
/// @param obs A pointer to the observable.
705
/// @param tol The tolerance below which coefficients are considered to be zero.
706
///
707
/// @return The canonical representation of the observable.
708
///
709
/// # Example
710
///
711
///     QkObs *iden = qk_obs_identity(100);
712
///     QkObs *two = qk_obs_add(iden, iden);
713
///
714
///     double tol = 1e-6;
715
///     QkObs *canonical = qk_obs_canonicalize(two);
716
///
717
/// # Safety
718
///
719
/// Behavior is undefined ``obs`` is not a valid, non-null pointer to a ``QkObs``.
720
#[no_mangle]
721
#[cfg(feature = "cbinding")]
722
pub unsafe extern "C" fn qk_obs_canonicalize(
×
723
    obs: *const SparseObservable,
×
724
    tol: f64,
×
725
) -> *mut SparseObservable {
×
726
    // SAFETY: Per documentation, the pointer is non-null and aligned.
×
727
    let obs = unsafe { const_ptr_as_ref(obs) };
×
728

×
729
    let result = obs.canonicalize(tol);
×
730
    Box::into_raw(Box::new(result))
×
731
}
×
732

733
/// @ingroup QkObs
734
/// Copy the observable.
735
///
736
/// @param obs A pointer to the observable.
737
///
738
/// @return A pointer to a copy of the observable.
739
///
740
/// # Example
741
///
742
///     QkObs *original = qk_obs_identity(100);
743
///     QkObs *copied = qk_obs_copy(original);
744
///
745
/// # Safety
746
///
747
/// Behavior is undefined ``obs`` is not a valid, non-null pointer to a ``QkObs``.
748
#[no_mangle]
749
#[cfg(feature = "cbinding")]
750
pub unsafe extern "C" fn qk_obs_copy(obs: *const SparseObservable) -> *mut SparseObservable {
×
751
    // SAFETY: Per documentation, the pointer is non-null and aligned.
×
752
    let obs = unsafe { const_ptr_as_ref(obs) };
×
753

×
754
    let copied = obs.clone();
×
755
    Box::into_raw(Box::new(copied))
×
756
}
×
757

758
/// @ingroup QkObs
759
/// Compare two observables for equality.
760
///
761
/// Note that this does not compare mathematical equality, but data equality. This means
762
/// that two observables might represent the same observable but not compare as equal.
763
///
764
/// @param obs A pointer to one observable.
765
/// @param other A pointer to another observable.
766
///
767
/// @return ``true`` if the observables are equal, ``false`` otherwise.
768
///
769
/// # Example
770
///
771
///     QkObs *observable = qk_obs_identity(100);
772
///     QkObs *other = qk_obs_identity(100);
773
///     bool are_equal = qk_obs_equal(observable, other);
774
///
775
/// # Safety
776
///
777
/// Behavior is undefined if ``obs`` or ``other`` are not valid, non-null pointers to
778
/// ``QkObs``\ s.
779
#[no_mangle]
780
#[cfg(feature = "cbinding")]
781
pub unsafe extern "C" fn qk_obs_equal(
×
782
    obs: *const SparseObservable,
×
783
    other: *const SparseObservable,
×
784
) -> bool {
×
785
    // SAFETY: Per documentation, the pointers are non-null and aligned.
×
786
    let obs = unsafe { const_ptr_as_ref(obs) };
×
787
    let other = unsafe { const_ptr_as_ref(other) };
×
788

×
789
    obs.eq(other)
×
790
}
×
791

792
/// @ingroup QkObs
793
/// Return a string representation of a ``QkObs``.
794
///
795
/// @param obs A pointer to the ``QkObs`` to get the string for.
796
///
797
/// @return A pointer to a nul-terminated char array of the string representation for ``obs``
798
///
799
/// # Example
800
///
801
///     QkObs *obs = qk_obs_identity(100);
802
///     char *string = qk_obs_str(obs);
803
///     qk_str_free(string);
804
///
805
/// # Safety
806
///
807
/// Behavior is undefined ``obs`` is not a valid, non-null pointer to a ``QkObs``.
808
///
809
/// The string must not be freed with the normal C free, you must use ``qk_str_free`` to
810
/// free the memory consumed by the String. Not calling ``qk_str_free`` will lead to a
811
/// memory leak.
812
///
813
/// Do not change the length of the string after it's returned (by writing a nul byte somewhere
814
/// inside the string or removing the final one), although values can be mutated.
815
#[no_mangle]
816
#[cfg(feature = "cbinding")]
817
pub unsafe extern "C" fn qk_obs_str(obs: *const SparseObservable) -> *mut c_char {
×
818
    // SAFETY: Per documentation, the pointer is non-null and aligned.
×
819
    let obs = unsafe { const_ptr_as_ref(obs) };
×
820
    let string: String = format!("{:?}", obs);
×
821
    CString::new(string).unwrap().into_raw()
×
822
}
×
823

824
/// @ingroup QkObs
825
/// Free a string representation.
826
///
827
/// @param string A pointer to the returned string representation from ``qk_obs_str`` or
828
///     ``qk_obsterm_str``.
829
///
830
/// # Safety
831
///
832
/// Behavior is undefined if ``str`` is not a pointer returned by ``qk_obs_str`` or
833
/// ``qk_obsterm_str``.
834
#[no_mangle]
835
#[cfg(feature = "cbinding")]
836
pub unsafe extern "C" fn qk_str_free(string: *mut c_char) {
×
837
    unsafe {
×
838
        let _ = CString::from_raw(string);
×
839
    }
×
840
}
×
841

842
/// @ingroup QkObsTerm
843
/// Return a string representation of the sparse term.
844
///
845
/// @param term A pointer to the term.
846
///
847
/// @return The function exit code. This is ``>0`` if reading the term failed.
848
///
849
/// # Example
850
///
851
///     QkObs *obs = qk_obs_identity(100);
852
///     QkObsTerm term;
853
///     qk_obs_term(obs, 0, &term);
854
///     char *string = qk_obsterm_str(&term);
855
///     qk_str_free(string);
856
///
857
/// # Safety
858
///
859
/// Behavior is undefined ``term`` is not a valid, non-null pointer to a ``QkObsTerm``.
860
///
861
/// The string must not be freed with the normal C free, you must use ``qk_str_free`` to
862
/// free the memory consumed by the String. Not calling ``qk_str_free`` will lead to a
863
/// memory leak.
864
///
865
/// Do not change the length of the string after it's returned, although values can be mutated.
866
#[no_mangle]
867
#[cfg(feature = "cbinding")]
868
pub unsafe extern "C" fn qk_obsterm_str(term: *const CSparseTerm) -> *mut c_char {
×
869
    // SAFETY: Per documentation, the pointer is non-null and aligned.
×
870
    let term = unsafe { const_ptr_as_ref(term) };
×
871

×
872
    let view: SparseTermView = term.try_into().unwrap();
×
873
    let string: String = format!("{:?}", view);
×
874
    CString::new(string).unwrap().into_raw()
×
875
}
×
876

877
/// @ingroup QkBitTerm
878
/// Get the label for a bit term.
879
///
880
/// @param bit_term The bit term.
881
///
882
/// @return The label as ``uint8_t``, which can be cast to ``char`` to obtain the character.
883
///
884
/// # Example
885
///
886
///     QkBitTerm bit_term = QkBitTerm_Y;
887
///     // cast the uint8_t to char
888
///     char label = qk_bitterm_label(bit_term);
889
///
890
/// # Safety
891
///
892
/// The behavior is undefined if ``bit_term`` is not a valid ``uint8_t`` value of a ``QkBitTerm``.
893
#[no_mangle]
894
#[cfg(feature = "cbinding")]
895
pub extern "C" fn qk_bitterm_label(bit_term: BitTerm) -> u8 {
×
896
    // BitTerm is implemented as u8, which is calling convention compatible with C,
×
897
    // hence we can pass ``bit_term`` by value
×
898
    bit_term
×
899
        .py_label()
×
900
        .chars()
×
901
        .next()
×
902
        .expect("Label has exactly one character") as u8
×
903
}
×
904

905
/// @ingroup QkObs
906
/// Convert to a Python-space ``SparseObservable``.
907
///
908
/// @param obs The C-space ``QkObs`` pointer.
909
///
910
/// @return A Python object representing the ``SparseObservable``.
911
///
912
/// # Safety
913
///
914
/// Behavior is undefined if ``obs`` is not a valid, non-null pointer to a ``QkObs``.
915
///
916
/// It is assumed that the thread currently executing this function holds the
917
/// Python GIL this is required to create the Python object returned by this
918
/// function.
919
#[no_mangle]
920
#[cfg(feature = "python_binding")]
921
#[cfg(feature = "cbinding")]
922
pub unsafe extern "C" fn qk_obs_to_python(obs: *const SparseObservable) -> *mut PyObject {
×
923
    // SAFETY: Per documentation, the pointer is non-null and aligned.
×
924
    let obs = unsafe { const_ptr_as_ref(obs) };
×
925
    let py_obs: PySparseObservable = obs.clone().into();
×
926

×
927
    // SAFETY: the C caller is required to hold the GIL.
×
928
    unsafe {
×
929
        let py = Python::assume_gil_acquired();
×
930
        Py::new(py, py_obs)
×
931
            .expect("Unable to create a Python object")
×
932
            .into_ptr()
×
933
    }
×
934
}
×
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