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

Qiskit / qiskit / 22907848122

10 Mar 2026 02:36PM UTC coverage: 87.619% (-0.1%) from 87.72%
22907848122

Pull #15723

github

web-flow
Merge affdb5058 into 7e3ecd216
Pull Request #15723: Expose Rust Custom Gates to Python

275 of 457 new or added lines in 16 files covered. (60.18%)

15 existing lines in 6 files now uncovered.

101390 of 115717 relevant lines covered (87.62%)

1166729.05 hits per line

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

85.81
/crates/cext/src/circuit.rs
1
// This code is part of Qiskit.
2
//
3
// (C) Copyright IBM 2025
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 https://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::{CStr, CString, c_char};
14

15
use crate::dag::COperationKind;
16
use crate::exit_codes::ExitCode;
17
use crate::pointers::{const_ptr_as_ref, mut_ptr_as_ref};
18

19
use nalgebra::{Matrix2, Matrix4};
20
use ndarray::{Array2, ArrayView2};
21
use num_complex::{Complex64, ComplexFloat};
22

23
use qiskit_circuit::bit::{ClassicalRegister, QuantumRegister};
24
use qiskit_circuit::bit::{ShareableClbit, ShareableQubit};
25
use qiskit_circuit::circuit_data::CircuitData;
26
use qiskit_circuit::dag_circuit::DAGCircuit;
27
use qiskit_circuit::instruction::Parameters;
28
use qiskit_circuit::interner::Interner;
29
use qiskit_circuit::operations::{
30
    ArrayType, DelayUnit, Operation, OperationRef, Param, StandardGate, StandardInstruction,
31
    UnitaryGate,
32
};
33
use qiskit_circuit::packed_instruction::{PackedInstruction, PackedOperation};
34
use qiskit_circuit::{BlocksMode, Clbit, Qubit, VarsMode};
35
use smallvec::smallvec;
36

37
/// @ingroup QkCircuit
38
/// Construct a new circuit with the given number of qubits and clbits.
39
///
40
/// @param num_qubits The number of qubits the circuit contains.
41
/// @param num_clbits The number of clbits the circuit contains.
42
///
43
/// @return A pointer to the created circuit.
44
///
45
/// # Example
46
///
47
///     QkCircuit *empty = qk_circuit_new(100, 100);
48
///
49
#[unsafe(no_mangle)]
50
pub extern "C" fn qk_circuit_new(num_qubits: u32, num_clbits: u32) -> *mut CircuitData {
382✔
51
    let qubits = if num_qubits > 0 {
382✔
52
        Some(
53
            (0..num_qubits)
370✔
54
                .map(|_| ShareableQubit::new_anonymous())
21,372✔
55
                .collect::<Vec<_>>(),
370✔
56
        )
57
    } else {
58
        None
12✔
59
    };
60
    let clbits = if num_clbits > 0 {
382✔
61
        Some(
62
            (0..num_clbits)
88✔
63
                .map(|_| ShareableClbit::new_anonymous())
20,524✔
64
                .collect::<Vec<_>>(),
88✔
65
        )
66
    } else {
67
        None
294✔
68
    };
69

70
    let circuit = CircuitData::new(qubits, clbits, (0.).into()).unwrap();
382✔
71
    Box::into_raw(Box::new(circuit))
382✔
72
}
382✔
73

74
/// @ingroup QkQuantumRegister
75
/// Construct a new owning quantum register with a given number of qubits and name
76
///
77
/// @param num_qubits The number of qubits to create the register for
78
/// @param name The name string for the created register. The name must be comprised of
79
/// valid UTF-8 characters.
80
///
81
/// @return A pointer to the created register
82
///
83
/// # Example
84
/// ```c
85
///     QkQuantumRegister *qr = qk_quantum_register_new(5, "five_qubits");
86
/// ```
87
///
88
/// # Safety
89
///
90
/// The `name` parameter must be a pointer to memory that contains a valid
91
/// nul terminator at the end of the string. It also must be valid for reads of
92
/// bytes up to and including the nul terminator.
93
#[unsafe(no_mangle)]
94
pub unsafe extern "C" fn qk_quantum_register_new(
192✔
95
    num_qubits: u32,
192✔
96
    name: *const c_char,
192✔
97
) -> *mut QuantumRegister {
192✔
98
    let name = unsafe {
192✔
99
        CStr::from_ptr(name)
192✔
100
            .to_str()
192✔
101
            .expect("Invalid UTF-8 character")
192✔
102
            .to_string()
192✔
103
    };
104
    // SAFETY: Per documentation the pointer for name is a valid CStr pointer
105
    let reg = QuantumRegister::new_owning(name, num_qubits);
192✔
106
    Box::into_raw(Box::new(reg))
192✔
107
}
192✔
108

109
/// @ingroup QkQuantumRegister
110
/// Free a quantum register.
111
///
112
/// @param reg A pointer to the register to free.
113
///
114
/// # Example
115
/// ```c
116
///     QkQuantumRegister *qr = qk_quantum_register_new(1024, "qreg");
117
///     qk_quantum_register_free(qr);
118
/// ```
119
///
120
/// # Safety
121
///
122
/// Behavior is undefined if ``reg`` is not either null or a valid pointer to a
123
/// ``QkQuantumRegister``.
124
#[unsafe(no_mangle)]
125
pub unsafe extern "C" fn qk_quantum_register_free(reg: *mut QuantumRegister) {
168✔
126
    if !reg.is_null() {
168✔
127
        if !reg.is_aligned() {
168✔
128
            panic!("Attempted to free a non-aligned pointer.")
×
129
        }
168✔
130

131
        // SAFETY: We have verified the pointer is non-null and aligned, so it should be
132
        // readable by Box.
133
        unsafe {
168✔
134
            let _ = Box::from_raw(reg);
168✔
135
        }
168✔
136
    }
×
137
}
168✔
138

139
/// @ingroup QkClassicalRegister
140
/// Free a classical register.
141
///
142
/// @param reg A pointer to the register to free.
143
///
144
/// # Example
145
/// ```c
146
///     QkClassicalRegister *cr = qk_classical_register_new(1024, "creg");
147
///     qk_classical_register_free(cr);
148
/// ```
149
///
150
/// # Safety
151
///
152
/// Behavior is undefined if ``reg`` is not either null or a valid pointer to a
153
/// ``QkClassicalRegister``.
154
#[unsafe(no_mangle)]
155
pub unsafe extern "C" fn qk_classical_register_free(reg: *mut ClassicalRegister) {
36✔
156
    if !reg.is_null() {
36✔
157
        if !reg.is_aligned() {
36✔
158
            panic!("Attempted to free a non-aligned pointer.")
×
159
        }
36✔
160

161
        // SAFETY: We have verified the pointer is non-null and aligned, so it should be
162
        // readable by Box.
163
        unsafe {
36✔
164
            let _ = Box::from_raw(reg);
36✔
165
        }
36✔
166
    }
×
167
}
36✔
168

169
/// @ingroup QkClassicalRegister
170
/// Construct a new owning classical register with a given number of clbits and name
171
///
172
/// @param num_clbits The number of clbits to create the register for
173
/// @param name The name string for the created register. The name must be comprised of
174
/// valid UTF-8 characters.
175
///
176
/// @return A pointer to the created register
177
///
178
/// # Example
179
/// ```c
180
///     QkClassicalRegister *cr = qk_classical_register_new(5, "five_qubits");
181
/// ```
182
///
183
/// # Safety
184
///
185
/// The `name` parameter must be a pointer to memory that contains a valid
186
/// nul terminator at the end of the string. It also must be valid for reads of
187
/// bytes up to and including the nul terminator.
188
#[unsafe(no_mangle)]
189
pub unsafe extern "C" fn qk_classical_register_new(
36✔
190
    num_clbits: u32,
36✔
191
    name: *const c_char,
36✔
192
) -> *mut ClassicalRegister {
36✔
193
    // SAFETY: Per documentation the pointer for name is a valid CStr pointer
194
    let name = unsafe {
36✔
195
        CStr::from_ptr(name)
36✔
196
            .to_str()
36✔
197
            .expect("Invalid UTF-8 character")
36✔
198
            .to_string()
36✔
199
    };
200
    let reg = ClassicalRegister::new_owning(name, num_clbits);
36✔
201
    Box::into_raw(Box::new(reg))
36✔
202
}
36✔
203

204
/// @ingroup QkCircuit
205
/// Add a quantum register to a given quantum circuit
206
///
207
/// @param circuit A pointer to the circuit.
208
/// @param reg A pointer to the quantum register
209
///
210
/// # Example
211
/// ```c
212
///     QkCircuit *qc = qk_circuit_new(0, 0);
213
///     QkQuantumRegister *qr = qk_quantum_register_new(1024, "my_little_register");
214
///     qk_circuit_add_quantum_register(qc, qr);
215
///     qk_quantum_register_free(qr);
216
///     qk_circuit_free(qc);
217
/// ```
218
///
219
/// # Safety
220
///
221
/// Behavior is undefined if ``circuit`` is not a valid, non-null pointer to a ``QkCircuit`` and
222
/// if ``reg`` is not a valid, non-null pointer to a ``QkQuantumRegister``.
223
#[unsafe(no_mangle)]
224
pub unsafe extern "C" fn qk_circuit_add_quantum_register(
4✔
225
    circuit: *mut CircuitData,
4✔
226
    reg: *const QuantumRegister,
4✔
227
) {
4✔
228
    // SAFETY: Per documentation, the pointer is non-null and aligned.
229
    let circuit = unsafe { mut_ptr_as_ref(circuit) };
4✔
230
    let qreg = unsafe { const_ptr_as_ref(reg) };
4✔
231

232
    circuit
4✔
233
        .add_qreg(qreg.clone(), true)
4✔
234
        .expect("Invalid register unable to be added to circuit");
4✔
235
}
4✔
236

237
/// @ingroup QkCircuit
238
/// Add a classical register to a given quantum circuit
239
///
240
/// @param circuit A pointer to the circuit.
241
/// @param reg A pointer to the classical register
242
///
243
/// # Example
244
/// ```c
245
///     QkCircuit *qc = qk_circuit_new(0, 0);
246
///     QkClassicalRegister *cr = qk_classical_register_new(24, "my_big_register");
247
///     qk_circuit_add_classical_register(qc, cr);
248
///     qk_classical_register_free(cr);
249
///     qk_circuit_free(qc);
250
/// ```
251
///
252
/// # Safety
253
///
254
/// Behavior is undefined if ``circuit`` is not a valid, non-null pointer to a ``QkCircuit`` and
255
/// if ``reg`` is not a valid, non-null pointer to a ``QkClassicalRegister``.
256
#[unsafe(no_mangle)]
257
pub unsafe extern "C" fn qk_circuit_add_classical_register(
4✔
258
    circuit: *mut CircuitData,
4✔
259
    reg: *const ClassicalRegister,
4✔
260
) {
4✔
261
    // SAFETY: Per documentation, the pointer is non-null and aligned.
262
    let circuit = unsafe { mut_ptr_as_ref(circuit) };
4✔
263
    let creg = unsafe { const_ptr_as_ref(reg) };
4✔
264

265
    circuit
4✔
266
        .add_creg(creg.clone(), true)
4✔
267
        .expect("Invalid register unable to be added to circuit");
4✔
268
}
4✔
269

270
/// @ingroup QkCircuit
271
/// Create a copy of a ``QkCircuit``.
272
///
273
/// @param circuit A pointer to the circuit to copy.
274
///
275
/// @return A new pointer to a copy of the input ``circuit``.
276
///
277
/// # Example
278
/// ```c
279
///     QkCircuit *qc = qk_circuit_new(100, 100);
280
///     QkCircuit *copy = qk_circuit_copy(qc);
281
/// ```
282
///
283
/// # Safety
284
///
285
/// Behavior is undefined if ``circuit`` is not a valid, non-null pointer to a ``QkCircuit``.
286
#[unsafe(no_mangle)]
287
pub unsafe extern "C" fn qk_circuit_copy(circuit: *const CircuitData) -> *mut CircuitData {
8✔
288
    // SAFETY: Per documentation, the pointer is non-null and aligned.
289
    let circuit = unsafe { const_ptr_as_ref(circuit) };
8✔
290
    Box::into_raw(Box::new(circuit.clone()))
8✔
291
}
8✔
292

293
/// @ingroup QkCircuit
294
/// Get the number of qubits the circuit contains.
295
///
296
/// @param circuit A pointer to the circuit.
297
///
298
/// @return The number of qubits the circuit is defined on.
299
///
300
/// # Example
301
/// ```c
302
///     QkCircuit *qc = qk_circuit_new(100, 100);
303
///     uint32_t num_qubits = qk_circuit_num_qubits(qc);  // num_qubits==100
304
/// ```
305
///
306
/// # Safety
307
///
308
/// Behavior is undefined if ``circuit`` is not a valid, non-null pointer to a ``QkCircuit``.
309
#[unsafe(no_mangle)]
310
pub unsafe extern "C" fn qk_circuit_num_qubits(circuit: *const CircuitData) -> u32 {
372✔
311
    // SAFETY: Per documentation, the pointer is non-null and aligned.
312
    let circuit = unsafe { const_ptr_as_ref(circuit) };
372✔
313

314
    circuit.num_qubits() as u32
372✔
315
}
372✔
316

317
/// @ingroup QkCircuit
318
/// Get the number of clbits the circuit contains.
319
///
320
/// @param circuit A pointer to the circuit.
321
///
322
/// @return The number of qubits the circuit is defined on.
323
///
324
/// # Example
325
/// ```c
326
///     QkCircuit *qc = qk_circuit_new(100, 50);
327
///     uint32_t num_clbits = qk_circuit_num_clbits(qc);  // num_clbits==50
328
/// ```
329
///
330
/// # Safety
331
///
332
/// Behavior is undefined if ``circuit`` is not a valid, non-null pointer to a ``QkCircuit``.
333
#[unsafe(no_mangle)]
334
pub unsafe extern "C" fn qk_circuit_num_clbits(circuit: *const CircuitData) -> u32 {
20✔
335
    // SAFETY: Per documentation, the pointer is non-null and aligned.
336
    let circuit = unsafe { const_ptr_as_ref(circuit) };
20✔
337

338
    circuit.num_clbits() as u32
20✔
339
}
20✔
340

341
/// @ingroup QkCircuit
342
/// Free the circuit.
343
///
344
/// @param circuit A pointer to the circuit to free.
345
///
346
/// # Example
347
/// ```c
348
///     QkCircuit *qc = qk_circuit_new(100, 100);
349
///     qk_circuit_free(qc);
350
/// ```
351
///
352
/// # Safety
353
///
354
/// Behavior is undefined if ``circuit`` is not either null or a valid pointer to a
355
/// ``QkCircuit``.
356
#[unsafe(no_mangle)]
357
pub unsafe extern "C" fn qk_circuit_free(circuit: *mut CircuitData) {
442✔
358
    if !circuit.is_null() {
442✔
359
        if !circuit.is_aligned() {
442✔
360
            panic!("Attempted to free a non-aligned pointer.")
×
361
        }
442✔
362

363
        // SAFETY: We have verified the pointer is non-null and aligned, so it should be
364
        // readable by Box.
365
        unsafe {
442✔
366
            let _ = Box::from_raw(circuit);
442✔
367
        }
442✔
368
    }
×
369
}
442✔
370

371
/// @ingroup QkCircuit
372
/// Append a ``QkGate`` to the circuit.
373
///
374
/// @param circuit A pointer to the circuit to add the gate to.
375
/// @param gate The StandardGate to add to the circuit.
376
/// @param qubits The pointer to the array of ``uint32_t`` qubit indices to add the gate on. This
377
///     can be a null pointer if there are no qubits for ``gate`` (e.g. ``QkGate_GlobalPhase``).
378
/// @param params The pointer to the array of ``double`` values to use for the gate parameters.
379
///     This can be a null pointer if there are no parameters for ``gate`` (e.g. ``QkGate_H``).
380
///
381
/// @return An exit code.
382
///
383
/// # Example
384
/// ```c
385
///     QkCircuit *qc = qk_circuit_new(100, 0);
386
///     uint32_t qubit[1] = {0};
387
///     qk_circuit_gate(qc, QkGate_H, qubit, NULL);
388
/// ```
389
///
390
/// # Safety
391
///
392
/// The ``qubits`` and ``params`` types are expected to be a pointer to an array of ``uint32_t``
393
/// and ``double`` respectively where the length is matching the expectations for the standard
394
/// gate. If the array is insufficiently long the behavior of this function is undefined as this
395
/// will read outside the bounds of the array. It can be a null pointer if there are no qubits
396
/// or params for a given gate. You can check ``qk_gate_num_qubits`` and ``qk_gate_num_params`` to
397
/// determine how many qubits and params are required for a given gate.
398
///
399
/// Behavior is undefined if ``circuit`` is not a valid, non-null pointer to a ``QkCircuit``.
400
#[unsafe(no_mangle)]
401
pub unsafe extern "C" fn qk_circuit_gate(
42,196✔
402
    circuit: *mut CircuitData,
42,196✔
403
    gate: StandardGate,
42,196✔
404
    qubits: *const u32,
42,196✔
405
    params: *const f64,
42,196✔
406
) -> ExitCode {
42,196✔
407
    // SAFETY: Per documentation, the pointer is non-null and aligned.
408
    let circuit = unsafe { mut_ptr_as_ref(circuit) };
42,196✔
409
    // SAFETY: Per the documentation the qubits and params pointers are arrays of num_qubits()
410
    // and num_params() elements respectively.
411
    unsafe {
412
        let qargs: &[Qubit] = match gate.num_qubits() {
42,196✔
413
            0 => &[],
×
414
            1 => &[Qubit(*qubits.wrapping_add(0))],
32,868✔
415
            2 => &[
9,324✔
416
                Qubit(*qubits.wrapping_add(0)),
9,324✔
417
                Qubit(*qubits.wrapping_add(1)),
9,324✔
418
            ],
9,324✔
419
            3 => &[
4✔
420
                Qubit(*qubits.wrapping_add(0)),
4✔
421
                Qubit(*qubits.wrapping_add(1)),
4✔
422
                Qubit(*qubits.wrapping_add(2)),
4✔
423
            ],
4✔
424
            4 => &[
×
425
                Qubit(*qubits.wrapping_add(0)),
×
426
                Qubit(*qubits.wrapping_add(1)),
×
427
                Qubit(*qubits.wrapping_add(2)),
×
428
                Qubit(*qubits.wrapping_add(3)),
×
429
            ],
×
430
            // There are no ``QkGate``s > 4 qubits
431
            _ => unreachable!(),
×
432
        };
433
        let params: &[Param] = match gate.num_params() {
42,196✔
434
            0 => &[],
41,756✔
435
            1 => &[(*params.wrapping_add(0)).into()],
428✔
436
            2 => &[
×
437
                (*params.wrapping_add(0)).into(),
×
438
                (*params.wrapping_add(1)).into(),
×
439
            ],
×
440
            3 => &[
12✔
441
                (*params.wrapping_add(0)).into(),
12✔
442
                (*params.wrapping_add(1)).into(),
12✔
443
                (*params.wrapping_add(2)).into(),
12✔
444
            ],
12✔
445
            4 => &[
×
446
                (*params.wrapping_add(0)).into(),
×
447
                (*params.wrapping_add(1)).into(),
×
448
                (*params.wrapping_add(2)).into(),
×
449
                (*params.wrapping_add(3)).into(),
×
450
            ],
×
451
            // There are no ``QkGate``s that take > 4 params
452
            _ => unreachable!(),
×
453
        };
454
        circuit.push_standard_gate(gate, params, qargs).unwrap()
42,196✔
455
    }
456
    ExitCode::Success
42,196✔
457
}
42,196✔
458

459
/// @ingroup QkCircuit
460
/// Get the number of qubits for a ``QkGate``.
461
///
462
/// @param gate The ``QkGate`` to get the number of qubits for.
463
///
464
/// @return The number of qubits the gate acts on.
465
///
466
/// # Example
467
/// ```c
468
///     uint32_t num_qubits = qk_gate_num_qubits(QkGate_CCX);
469
/// ```
470
///
471
#[unsafe(no_mangle)]
472
pub extern "C" fn qk_gate_num_qubits(gate: StandardGate) -> u32 {
208✔
473
    gate.num_qubits()
208✔
474
}
208✔
475

476
/// @ingroup QkCircuit
477
/// Get the number of parameters for a ``QkGate``.
478
///
479
/// @param gate The ``QkGate`` to get the number of qubits for.
480
///
481
/// @return The number of parameters the gate has.
482
///
483
/// # Example
484
/// ```c
485
///     uint32_t num_params = qk_gate_num_params(QkGate_R);
486
/// ```
487
///
488
#[unsafe(no_mangle)]
489
pub extern "C" fn qk_gate_num_params(gate: StandardGate) -> u32 {
208✔
490
    gate.num_params()
208✔
491
}
208✔
492

493
/// @ingroup QkCircuit
494
/// Append a measurement to the circuit
495
///
496
/// @param circuit A pointer to the circuit to add the measurement to
497
/// @param qubit The ``uint32_t`` for the qubit to measure
498
/// @param clbit The ``uint32_t`` for the clbit to store the measurement outcome in
499
///
500
/// @return An exit code.
501
///
502
/// # Example
503
/// ```c
504
///     QkCircuit *qc = qk_circuit_new(100, 1);
505
///     qk_circuit_measure(qc, 0, 0);
506
/// ```
507
///
508
/// # Safety
509
///
510
/// Behavior is undefined if ``circuit`` is not a valid, non-null pointer to a ``QkCircuit``.
511
#[unsafe(no_mangle)]
512
pub unsafe extern "C" fn qk_circuit_measure(
12,532✔
513
    circuit: *mut CircuitData,
12,532✔
514
    qubit: u32,
12,532✔
515
    clbit: u32,
12,532✔
516
) -> ExitCode {
12,532✔
517
    // SAFETY: Per documentation, the pointer is non-null and aligned.
518
    let circuit = unsafe { mut_ptr_as_ref(circuit) };
12,532✔
519
    circuit
12,532✔
520
        .push_packed_operation(
12,532✔
521
            PackedOperation::from_standard_instruction(StandardInstruction::Measure),
12,532✔
522
            None,
12,532✔
523
            &[Qubit(qubit)],
12,532✔
524
            &[Clbit(clbit)],
12,532✔
525
        )
526
        .unwrap();
12,532✔
527
    ExitCode::Success
12,532✔
528
}
12,532✔
529

530
/// @ingroup QkCircuit
531
/// Append a reset to the circuit
532
///
533
/// @param circuit A pointer to the circuit to add the reset to
534
/// @param qubit The ``uint32_t`` for the qubit to reset
535
///
536
/// @return An exit code.
537
///
538
/// # Example
539
/// ```c
540
///     QkCircuit *qc = qk_circuit_new(100, 0);
541
///     qk_circuit_reset(qc, 0);
542
/// ```
543
///
544
/// # Safety
545
///
546
/// Behavior is undefined if ``circuit`` is not a valid, non-null pointer to a ``QkCircuit``.
547
#[unsafe(no_mangle)]
548
pub unsafe extern "C" fn qk_circuit_reset(circuit: *mut CircuitData, qubit: u32) -> ExitCode {
4,000✔
549
    // SAFETY: Per documentation, the pointer is non-null and aligned.
550
    let circuit = unsafe { mut_ptr_as_ref(circuit) };
4,000✔
551
    circuit
4,000✔
552
        .push_packed_operation(
4,000✔
553
            PackedOperation::from_standard_instruction(StandardInstruction::Reset),
4,000✔
554
            None,
4,000✔
555
            &[Qubit(qubit)],
4,000✔
556
            &[],
4,000✔
557
        )
558
        .unwrap();
4,000✔
559
    ExitCode::Success
4,000✔
560
}
4,000✔
561

562
/// @ingroup QkCircuit
563
/// Append a barrier to the circuit.
564
///
565
/// @param circuit A pointer to the circuit to add the barrier to.
566
/// @param num_qubits The number of qubits wide the barrier is.
567
/// @param qubits The pointer to the array of ``uint32_t`` qubit indices to add the barrier on.
568
///
569
/// @return An exit code.
570
///
571
/// # Example
572
/// ```c
573
///     QkCircuit *qc = qk_circuit_new(100, 1);
574
///     uint32_t qubits[5] = {0, 1, 2, 3, 4};
575
///     qk_circuit_barrier(qc, qubits, 5);
576
/// ```
577
///
578
/// # Safety
579
///
580
/// The length of the array ``qubits`` points to must be ``num_qubits``. If there is
581
/// a mismatch the behavior is undefined.
582
///
583
/// Behavior is undefined if ``circuit`` is not a valid, non-null pointer to a ``QkCircuit``.
584
#[unsafe(no_mangle)]
585
pub unsafe extern "C" fn qk_circuit_barrier(
108✔
586
    circuit: *mut CircuitData,
108✔
587
    qubits: *const u32,
108✔
588
    num_qubits: u32,
108✔
589
) -> ExitCode {
108✔
590
    // SAFETY: Per documentation, the pointer is non-null and aligned.
591
    let circuit = unsafe { mut_ptr_as_ref(circuit) };
108✔
592
    // SAFETY: Per the documentation the qubits pointer is an array of num_qubits elements
593
    let qubits: Vec<Qubit> = unsafe {
108✔
594
        (0..num_qubits)
108✔
595
            .map(|idx| Qubit(*qubits.wrapping_add(idx as usize)))
16,388✔
596
            .collect()
108✔
597
    };
598
    circuit
108✔
599
        .push_packed_operation(
108✔
600
            PackedOperation::from_standard_instruction(StandardInstruction::Barrier(num_qubits)),
108✔
601
            None,
108✔
602
            &qubits,
108✔
603
            &[],
108✔
604
        )
605
        .unwrap();
108✔
606
    ExitCode::Success
108✔
607
}
108✔
608

609
/// An individual operation count represented by the operation name
610
/// and the number of instances in the circuit.
611
#[repr(C)]
612
pub struct OpCount {
613
    /// A nul terminated string representing the operation name
614
    name: *const c_char,
615
    /// The number of instances of this operation in the circuit
616
    count: usize,
617
}
618

619
/// An array of ``OpCount`` objects representing the total counts of all
620
/// the operation types in a circuit.
621
#[repr(C)]
622
pub struct OpCounts {
623
    /// A array of size ``len`` containing ``OpCount`` objects for each
624
    /// type of operation in the circuit
625
    data: *mut OpCount,
626
    /// The number of elements in ``data``
627
    len: usize,
628
}
629

630
#[inline]
631
fn conjugate(matrix: ArrayView2<Complex64>) -> Array2<Complex64> {
×
632
    Array2::from_shape_fn((matrix.nrows(), matrix.ncols()), |(i, j)| {
×
633
        matrix[(j, i)].conj()
×
634
    })
×
635
}
×
636

637
/// Check an [ArrayType] represents a unitary matrix. Uses an element-wise check; if
638
/// any element in ``conjugate(matrix) * matrix`` differs from the identity by more than ``tol``
639
/// (in magnitude), the matrix is not considered unitary.
640
fn is_unitary(matrix: &ArrayType, tol: f64) -> bool {
12✔
641
    let not_unitary = match matrix {
12✔
642
        ArrayType::OneQ(mat) => (mat.adjoint() * mat - Matrix2::identity())
×
643
            .iter()
×
644
            .any(|val| val.abs() > tol),
×
645
        ArrayType::TwoQ(mat) => (mat.adjoint() * mat - Matrix4::identity())
12✔
646
            .iter()
12✔
647
            .any(|val| val.abs() > tol),
132✔
648
        ArrayType::NDArray(mat) => {
×
649
            let product = mat.dot(&conjugate(mat.view()));
×
650
            product.indexed_iter().any(|((row, col), value)| {
×
651
                if row == col {
×
652
                    (value - Complex64::ONE).abs() > tol
×
653
                } else {
654
                    value.abs() > tol
×
655
                }
656
            })
×
657
        }
658
    };
659
    !not_unitary // using double negation to use ``any`` (faster) instead of ``all``
12✔
660
}
12✔
661

662
/// Create a unitary matrix `ArrayType` from a pointer to a row-major contiguous matrix of the
663
/// correct dimensions.
664
///
665
/// If `tol` is `Some`, the unitary matrix is checked for tolerance against the given value.  If the
666
/// tolerance check fails, no array is returned.
667
///
668
/// The data is copied out of `matrix`.
669
///
670
/// # Safety
671
///
672
/// `matrix` must be aligned and valid for `4 ** num_qubits` reads.
673
pub(crate) unsafe fn unitary_from_pointer(
88✔
674
    matrix: *const Complex64,
88✔
675
    num_qubits: u32,
88✔
676
    tol: Option<f64>,
88✔
677
) -> Option<ArrayType> {
88✔
678
    let dim = 1 << num_qubits;
88✔
679
    // SAFETY: per documentation, `matrix` is aligned and valid for `4**num_qubits` reads.
680
    let raw = unsafe { ::std::slice::from_raw_parts(matrix, dim * dim) };
88✔
681
    let mat = match num_qubits {
88✔
682
        1 => ArrayType::OneQ(Matrix2::from_fn(|i, j| raw[i * dim + j])),
96✔
683
        2 => ArrayType::TwoQ(Matrix4::from_fn(|i, j| raw[i * dim + j])),
576✔
684
        _ => ArrayType::NDArray(Array2::from_shape_fn((dim, dim), |(i, j)| raw[i * dim + j])),
1,036✔
685
    };
686
    match tol {
88✔
687
        Some(tol) => is_unitary(&mat, tol).then_some(mat),
12✔
688
        None => Some(mat),
76✔
689
    }
690
}
88✔
691

692
/// @ingroup QkCircuit
693
/// Append an arbitrary unitary matrix to the circuit.
694
///
695
/// @param circuit A pointer to the circuit to append the unitary to.
696
/// @param matrix A pointer to the ``QkComplex64`` array representing the unitary matrix.
697
///     This must be a row-major, unitary matrix of dimension ``2 ^ num_qubits x 2 ^ num_qubits``.
698
///     More explicitly: the ``(i, j)``-th element is given by ``matrix[i * 2^n + j]``.
699
///     The contents of ``matrix`` are copied inside this function before being added to the circuit,
700
///     so caller keeps ownership of the original memory that ``matrix`` points to and can reuse it
701
///     after the call and the caller is responsible for freeing it.
702
/// @param qubits A pointer to array of qubit indices, of length ``num_qubits``.
703
/// @param num_qubits The number of qubits the unitary acts on.
704
/// @param check_input When true, the function verifies that the matrix is unitary.
705
///     If set to False the caller is responsible for ensuring the matrix is unitary, if
706
///     the matrix is not unitary this is undefined behavior and will result in a corrupt
707
///     circuit.
708
///
709
/// @return An exit code.
710
///
711
/// # Example
712
/// ```c
713
/// QkComplex64 c0 = {0, 0};  // 0+0i
714
/// QkComplex64 c1 = {1, 0};  // 1+0i
715
///
716
/// const uint32_t num_qubits = 1;
717
/// QkComplex64 unitary[2*2] = {c0, c1,  // row 0
718
///                             c1, c0}; // row 1
719
///
720
/// QkCircuit *circuit = qk_circuit_new(1, 0);  // 1 qubit circuit
721
/// uint32_t qubit[1] = {0};  // qubit to apply the unitary on
722
/// qk_circuit_unitary(circuit, unitary, qubit, num_qubits, true);
723
/// ```
724
///
725
/// # Safety
726
///
727
/// Behavior is undefined if any of the following is violated:
728
///
729
/// * ``circuit`` is a valid, non-null pointer to a ``QkCircuit``
730
/// * ``matrix`` is an aligned pointer to ``4**num_qubits`` initialized ``QkComplex64`` values
731
/// * ``qubits`` is an aligned pointer to ``num_qubits`` initialized ``uint32_t`` values
732
#[unsafe(no_mangle)]
733
pub unsafe extern "C" fn qk_circuit_unitary(
32✔
734
    circuit: *mut CircuitData,
32✔
735
    matrix: *const Complex64,
32✔
736
    qubits: *const u32,
32✔
737
    num_qubits: u32,
32✔
738
    check_input: bool,
32✔
739
) -> ExitCode {
32✔
740
    // SAFETY: Caller guarantees pointer validation, alignment
741
    let circuit = unsafe { mut_ptr_as_ref(circuit) };
32✔
742
    let mat = unsafe { unitary_from_pointer(matrix, num_qubits, check_input.then_some(1e-12)) };
32✔
743
    let Some(mat) = mat else {
32✔
744
        return ExitCode::ExpectedUnitary;
4✔
745
    };
746
    let qubits = if num_qubits == 0 {
28✔
747
        // This handles the case of C passing us a null pointer for the qubits; Rust slices
748
        // can't be backed by the null pointer even when empty.
749
        &[]
×
750
    } else {
751
        // SAFETY: per documentation, `qubits` is aligned and valid for `num_qubits` reads.  Per
752
        // previous check, `num_qubits` is nonzero so `qubits` cannot be null.
753
        unsafe { ::std::slice::from_raw_parts(qubits as *const Qubit, num_qubits as usize) }
28✔
754
    };
755

756
    // Create PackedOperation -> push to circuit_data
757
    let u_gate = Box::new(UnitaryGate { array: mat });
28✔
758
    let op = PackedOperation::from_unitary(u_gate);
28✔
759
    circuit
28✔
760
        .push_packed_operation(op, None, qubits, &[])
28✔
761
        .unwrap();
28✔
762
    // Return success
763
    ExitCode::Success
28✔
764
}
32✔
765

766
/// @ingroup QkCircuit
767
/// Copy out the unitary matrix for a given instruction in the circuit.
768
///
769
/// Panics if the instruction at the given index is not a unitary.
770
///
771
/// @param circuit A pointer to the circuit to get the unitary from.
772
/// @param index The index of the instruction to get the unitary for.
773
/// @param out Allocated and aligned pointer to write the unitary matrix to.
774
///
775
/// # Safety
776
///
777
/// Behavior is undefined if any of the following is violated:
778
/// if ``circuit`` is not a valid, non-null pointer to a ``QkCircuit``,
779
/// if ``index`` is out of bounds for the number of instructions in the circuit,
780
/// if `out` is not valid for `4**num_qubits` writes of `QkComplex64`.
781
#[unsafe(no_mangle)]
782
pub unsafe extern "C" fn qk_circuit_inst_unitary(
8✔
783
    circuit: *mut CircuitData,
8✔
784
    index: usize,
8✔
785
    out: *mut Complex64,
8✔
786
) {
8✔
787
    // SAFETY: Per documentation the pointer is to a valid, non-null QkCircuit.
788
    let circuit = unsafe { const_ptr_as_ref(circuit) };
8✔
789
    let instr: &PackedInstruction = &circuit.data()[index];
8✔
790
    let OperationRef::Unitary(unitary) = instr.op.view() else {
8✔
791
        panic!("Instruction at index {index} is not a unitary");
×
792
    };
793
    match &unitary.array {
8✔
794
        ArrayType::OneQ(array) => {
4✔
795
            let dim = 2;
4✔
796
            for row in 0..dim {
8✔
797
                for col in 0..dim {
16✔
798
                    // SAFETY: per documentation, `out` is aligned and valid for 4 writes.
16✔
799
                    unsafe { out.add(dim * row + col).write(array[(row, col)]) };
16✔
800
                }
16✔
801
            }
802
        }
803
        ArrayType::TwoQ(array) => {
×
804
            let dim = 4;
×
805
            for row in 0..dim {
×
806
                for col in 0..dim {
×
807
                    // SAFETY: per documentation, `out` is aligned and valid for 16 writes.
×
808
                    unsafe { out.add(dim * row + col).write(array[(row, col)]) };
×
809
                }
×
810
            }
811
        }
812
        ArrayType::NDArray(array) => {
4✔
813
            for (i, val) in array.iter().enumerate() {
256✔
814
                // SAFETY: per documentation, `out` is aligned and valid for `array.size()` writes.
256✔
815
                unsafe { out.add(i).write(*val) };
256✔
816
            }
256✔
817
        }
818
    }
819
}
8✔
820

821
/// @ingroup QkCircuit
822
/// Get the "kind" of circuit instruction.
823
///
824
/// @param circuit A pointer to the circuit to get the instruction kind from.
825
/// @param index The index of the instruction to get the kind for.
826
///
827
/// @return A ``QkOperationKind`` enum value representing the kind of instruction at the given
828
/// index.
829
///
830
/// # Safety
831
///
832
/// Behavior is undefined if ``circuit`` is not a valid, non-null pointer to a ``QkCircuit``
833
/// and if ``index`` is out of bounds for the number of instructions in the circuit.
834
#[unsafe(no_mangle)]
835
pub unsafe extern "C" fn qk_circuit_instruction_kind(
8✔
836
    circuit: *const CircuitData,
8✔
837
    index: usize,
8✔
838
) -> COperationKind {
8✔
839
    // SAFETY: Per documentation, the pointer is non-null and aligned.
840
    let circuit = unsafe { const_ptr_as_ref(circuit) };
8✔
841
    let instr: &PackedInstruction = &circuit.data()[index];
8✔
842
    match instr.op.view() {
8✔
843
        OperationRef::StandardGate(_) => COperationKind::Gate,
×
844
        OperationRef::StandardInstruction(instr) => match instr {
×
845
            StandardInstruction::Barrier(_) => COperationKind::Barrier,
×
846
            StandardInstruction::Delay(_) => COperationKind::Delay,
×
847
            StandardInstruction::Measure => COperationKind::Measure,
×
848
            StandardInstruction::Reset => COperationKind::Reset,
×
849
        },
850
        OperationRef::Unitary(_) => COperationKind::Unitary,
8✔
851
        OperationRef::PauliProductMeasurement(_) => COperationKind::PauliProductMeasurement,
×
852
        OperationRef::PauliProductRotation(_) => COperationKind::PauliProductRotation,
×
853
        OperationRef::ControlFlow(_) => COperationKind::ControlFlow,
×
854
        OperationRef::Gate(_)
855
        | OperationRef::Instruction(_)
856
        | OperationRef::Operation(_)
NEW
857
        | OperationRef::CustomOperation(_) => COperationKind::Unknown,
×
858
    }
859
}
8✔
860

861
/// @ingroup QkCircuit
862
/// Return a list of string names for instructions in a circuit and their counts.
863
///
864
/// To properly free the memory allocated by the struct, you should call ``qk_opcounts_clear``.
865
/// Dropping the ``QkOpCounts`` struct without doing so will leave the stored array of ``QkOpCount``
866
/// allocated and produce a memory leak.
867
///
868
/// @param circuit A pointer to the circuit to get the counts for.
869
///
870
/// @return An ``QkOpCounts`` struct containing the circuit operation counts.
871
///
872
/// # Example
873
/// ```c
874
///     QkCircuit *qc = qk_circuit_new(100, 0);
875
///     uint32_t qubits[1] = {0};
876
///     qk_circuit_gate(qc, QkGate_H, qubits, NULL);
877
///     QkOpCounts counts = qk_circuit_count_ops(qc);
878
///     // .. once done
879
///     qk_opcounts_clear(&counts);
880
/// ```
881
///
882
/// # Safety
883
///
884
/// Behavior is undefined if ``circuit`` is not a valid, non-null pointer to a ``QkCircuit``.
885
#[unsafe(no_mangle)]
886
pub unsafe extern "C" fn qk_circuit_count_ops(circuit: *const CircuitData) -> OpCounts {
116✔
887
    // SAFETY: Per documentation, the pointer is non-null and aligned.
888
    let circuit = unsafe { const_ptr_as_ref(circuit) };
116✔
889
    let count_ops = circuit.count_ops();
116✔
890
    let output = {
116✔
891
        let vec: Vec<OpCount> = count_ops
116✔
892
            .into_iter()
116✔
893
            .map(|(name, count)| OpCount {
116✔
894
                name: CString::new(name).unwrap().into_raw(),
224✔
895
                count,
224✔
896
            })
224✔
897
            .collect();
116✔
898
        vec.into_boxed_slice()
116✔
899
    };
900
    let len = output.len();
116✔
901
    let data = Box::into_raw(output) as *mut OpCount;
116✔
902
    OpCounts { data, len }
116✔
903
}
116✔
904

905
/// @ingroup QkCircuit
906
/// Return the total number of instructions in the circuit.
907
///
908
/// @param circuit A pointer to the circuit to get the total number of instructions for.
909
///
910
/// @return The total number of instructions in the circuit.
911
///
912
/// # Example
913
/// ```c
914
///     QkCircuit *qc = qk_circuit_new(100, 0);
915
///     uint32_t qubit[1] = {0};
916
///     qk_circuit_gate(qc, QkGate_H, qubit, NULL);
917
///     size_t num = qk_circuit_num_instructions(qc); // 1
918
/// ```
919
///
920
/// # Safety
921
///
922
/// Behavior is undefined if ``circuit`` is not a valid, non-null pointer to a ``QkCircuit``.
923
#[unsafe(no_mangle)]
924
pub unsafe extern "C" fn qk_circuit_num_instructions(circuit: *const CircuitData) -> usize {
812✔
925
    // SAFETY: Per documentation, the pointer is non-null and aligned.
926
    let circuit = unsafe { const_ptr_as_ref(circuit) };
812✔
927
    circuit.len()
812✔
928
}
812✔
929

930
/// A circuit instruction representation.
931
///
932
/// This struct represents the data contained in an individual instruction in a ``QkCircuit``.
933
/// It is not a pointer to the underlying object, but contains a copy of the properties of the
934
/// instruction for inspection.
935
#[repr(C)]
936
pub struct CInstruction {
937
    /// The instruction name
938
    name: *mut c_char,
939
    /// A pointer to an array of qubit indices this instruction operates on.
940
    qubits: *mut u32,
941
    /// A pointer to an array of clbit indices this instruction operates on.
942
    clbits: *mut u32,
943
    /// A pointer to an array of parameter values for this instruction.
944
    params: *mut f64,
945
    /// The number of qubits for this instruction.
946
    num_qubits: u32,
947
    /// The number of clbits for this instruction.
948
    num_clbits: u32,
949
    /// The number of parameters for this instruction.
950
    num_params: u32,
951
}
952
impl CInstruction {
953
    /// Create a `CInstruction` that owns pointers to copies of the information in the given
954
    /// `PackedInstruction`.
955
    ///
956
    /// This must be cleared by a call to `qk_circuit_instruction_clear` to avoid leaking its
957
    /// allocations.
958
    ///
959
    /// Panics if the operation name contains a nul, or if the instruction has non-float parameters.
960
    pub(crate) fn from_packed_instruction_with_floats(
18,964✔
961
        packed: &PackedInstruction,
18,964✔
962
        qargs_interner: &Interner<[Qubit]>,
18,964✔
963
        cargs_interner: &Interner<[Clbit]>,
18,964✔
964
    ) -> Self {
18,964✔
965
        let name = CString::new(packed.op.name())
18,964✔
966
            .expect("names do not contain nul")
18,964✔
967
            .into_raw();
18,964✔
968
        let qargs = qargs_interner.get(packed.qubits);
18,964✔
969
        let cargs = cargs_interner.get(packed.clbits);
18,964✔
970
        let params = packed
18,964✔
971
            .params_view()
18,964✔
972
            .iter()
18,964✔
973
            .map(|p| match p {
18,964✔
974
                Param::Float(p) => Some(*p),
492✔
975
                _ => None,
×
976
            })
492✔
977
            .collect::<Option<Box<[f64]>>>()
18,964✔
978
            .expect("caller is responsible for ensuring all parameters are floats");
18,964✔
979
        Self {
980
            name,
18,964✔
981
            num_qubits: qargs.len() as u32,
18,964✔
982
            qubits: Box::leak(qargs.iter().map(|q| q.0).collect::<Box<[u32]>>()).as_mut_ptr(),
18,964✔
983
            num_clbits: cargs.len() as u32,
18,964✔
984
            clbits: Box::leak(cargs.iter().map(|c| c.0).collect::<Box<[u32]>>()).as_mut_ptr(),
18,964✔
985
            num_params: params.len() as u32,
18,964✔
986
            params: Box::leak(params).as_mut_ptr(),
18,964✔
987
        }
988
    }
18,964✔
989
}
990

991
/// @ingroup QkCircuit
992
/// Return the instruction details for an instruction in the circuit.
993
///
994
/// This function is used to get the instruction details for a given instruction in
995
/// the circuit.
996
///
997
/// This function allocates memory internally for the provided ``QkCircuitInstruction``
998
/// and thus you are responsible for calling ``qk_circuit_instruction_clear`` to
999
/// free it.
1000
///
1001
/// @param circuit A pointer to the circuit to get the instruction details for.
1002
/// @param index The instruction index to get the instruction details of.
1003
/// @param instruction A pointer to where to write out the ``QkCircuitInstruction``
1004
///
1005
///
1006
/// # Example
1007
/// ```c
1008
///     QkCircuitInstruction inst;
1009
///     QkCircuit *qc = qk_circuit_new(100, 0);
1010
///     uint32_t qubit[1] = {0};
1011
///     qk_circuit_gate(qc, QkGate_H, qubit, NULL);
1012
///     qk_circuit_get_instruction(qc, 0, &inst);
1013
///     qk_circuit_instruction_clear(&inst);
1014
/// ```
1015
///
1016
/// # Safety
1017
///
1018
/// Behavior is undefined if ``circuit`` is not a valid, non-null pointer to a ``QkCircuit``. The
1019
/// value for ``index`` must be less than the value returned by ``qk_circuit_num_instructions``
1020
/// otherwise this function will panic. Behavior is undefined if ``instruction`` is not a valid,
1021
/// non-null pointer to a memory allocation with sufficient space for a ``QkCircuitInstruction``.
1022
#[unsafe(no_mangle)]
1023
pub unsafe extern "C" fn qk_circuit_get_instruction(
18,892✔
1024
    circuit: *const CircuitData,
18,892✔
1025
    index: usize,
18,892✔
1026
    instruction: *mut CInstruction,
18,892✔
1027
) {
18,892✔
1028
    // SAFETY: Per documentation, `circuit` is a pointer to valid data.
1029
    let circuit = unsafe { const_ptr_as_ref(circuit) };
18,892✔
1030
    let inst = CInstruction::from_packed_instruction_with_floats(
18,892✔
1031
        &circuit.data()[index],
18,892✔
1032
        circuit.qargs_interner(),
18,892✔
1033
        circuit.cargs_interner(),
18,892✔
1034
    );
1035
    // SAFETY: per documentation, `instruction` is a pointer to a sufficient allocation.
1036
    unsafe { instruction.write(inst) };
18,892✔
1037
}
18,892✔
1038

1039
/// @ingroup QkCircuit
1040
/// Clear the data in circuit instruction object.
1041
///
1042
/// This function doesn't free the allocation for the provided ``QkCircuitInstruction`` pointer, it
1043
/// only frees the internal allocations for the data contained in the instruction. You are
1044
/// responsible for allocating and freeing the actual allocation used to store a
1045
/// ``QkCircuitInstruction``.
1046
///
1047
/// @param inst A pointer to the instruction to free.
1048
///
1049
/// # Example
1050
/// ```c
1051
///     QkCircuitInstruction *inst = malloc(sizeof(QkCircuitInstruction));
1052
///     QkCircuit *qc = qk_circuit_new(100, 0);
1053
///     uint32_t q0[1] = {0};
1054
///     qk_circuit_gate(qc, QkGate_H, q0, NULL);
1055
///     qk_circuit_get_instruction(qc, 0, inst);
1056
///     qk_circuit_instruction_clear(inst); // clear internal allocations
1057
///     free(inst); // free struct
1058
///     qk_circuit_free(qc); // free the circuit
1059
/// ```
1060
///
1061
/// # Safety
1062
///
1063
/// Behavior is undefined if ``inst`` is not a valid, non-null pointer to a ``QkCircuitInstruction``.
1064
#[unsafe(no_mangle)]
1065
pub unsafe extern "C" fn qk_circuit_instruction_clear(inst: *mut CInstruction) {
18,964✔
1066
    // SAFETY: Loading the data from pointers contained in a CInstruction. These should only be
1067
    // created by rust code and are constructed from Vecs internally or CStrings.
1068
    unsafe {
1069
        let inst = mut_ptr_as_ref(inst);
18,964✔
1070
        if inst.num_qubits > 0 && !inst.qubits.is_null() {
18,964✔
1071
            let qubits = std::slice::from_raw_parts_mut(inst.qubits, inst.num_qubits as usize);
18,964✔
1072
            let _: Box<[u32]> = Box::from_raw(qubits as *mut [u32]);
18,964✔
1073
            inst.qubits = std::ptr::null_mut();
18,964✔
1074
        }
18,964✔
1075
        inst.num_qubits = 0;
18,964✔
1076
        if inst.num_clbits > 0 && !inst.clbits.is_null() {
18,964✔
1077
            let clbits = std::slice::from_raw_parts_mut(inst.clbits, inst.num_clbits as usize);
4,012✔
1078
            let _: Box<[u32]> = Box::from_raw(clbits as *mut [u32]);
4,012✔
1079
            inst.clbits = std::ptr::null_mut();
4,012✔
1080
        }
14,952✔
1081
        inst.num_clbits = 0;
18,964✔
1082
        if inst.num_params > 0 && !inst.params.is_null() {
18,964✔
1083
            let params = std::slice::from_raw_parts_mut(inst.params, inst.num_params as usize);
464✔
1084
            let _ = Box::from_raw(params as *mut [f64]);
464✔
1085
            inst.params = std::ptr::null_mut();
464✔
1086
        }
18,500✔
1087
        inst.num_params = 0;
18,964✔
1088
        if !inst.name.is_null() {
18,964✔
1089
            let _ = CString::from_raw(inst.name);
18,964✔
1090
            inst.name = std::ptr::null_mut();
18,964✔
1091
        }
18,964✔
1092
    }
1093
}
18,964✔
1094

1095
/// @ingroup QkCircuit
1096
/// Clear the content in a circuit operation count list.
1097
///
1098
/// @param op_counts The returned op count list from ``qk_circuit_count_ops``.
1099
///
1100
/// # Safety
1101
///
1102
/// Behavior is undefined if ``op_counts`` is not the object returned by ``qk_circuit_count_ops``.
1103
#[unsafe(no_mangle)]
1104
pub unsafe extern "C" fn qk_opcounts_clear(op_counts: *mut OpCounts) {
116✔
1105
    // SAFETY: The user guarantees the input is a valid OpCounts pointer.
1106
    let op_counts = unsafe { mut_ptr_as_ref(op_counts) };
116✔
1107

1108
    if op_counts.len > 0 && !op_counts.data.is_null() {
116✔
1109
        // SAFETY: We load the box from a slice pointer created from
1110
        // the raw parts from the OpCounts::data attribute.
1111
        unsafe {
1112
            let slice: Box<[OpCount]> = Box::from_raw(std::ptr::slice_from_raw_parts_mut(
108✔
1113
                op_counts.data,
108✔
1114
                op_counts.len,
108✔
1115
            ));
1116
            // free the allocated strings in each OpCount
1117
            for count in slice.iter() {
224✔
1118
                if !count.name.is_null() {
224✔
1119
                    let _ = CString::from_raw(count.name as *mut c_char);
224✔
1120
                }
224✔
1121
            }
1122
            // the variable vec goes out of bounds and is freed too
1123
        }
1124
    }
8✔
1125
    op_counts.len = 0;
116✔
1126
    op_counts.data = std::ptr::null_mut();
116✔
1127
}
116✔
1128

1129
/// @ingroup QkCircuit
1130
/// Convert to a Python-space ``QuantumCircuit``.
1131
///
1132
/// This function takes ownership of the pointer and gives it to Python. Using
1133
/// the input ``circuit`` pointer after it's passed to this function is
1134
/// undefined behavior. In particular, ``qk_circuit_free`` should not be called
1135
/// on this pointer anymore.
1136
///
1137
/// @param circuit The C-space ``QkCircuit`` pointer.
1138
///
1139
/// @return A Python ``QuantumCircuit`` object.
1140
///
1141
/// # Safety
1142
///
1143
/// Behavior is undefined if ``circuit`` is not a valid, non-null pointer to
1144
/// a ``QkCircuit``
1145
///
1146
/// It is assumed that the thread currently executing this function holds the
1147
/// Python GIL. This is required to create the Python object returned by this
1148
/// function.
1149
#[unsafe(no_mangle)]
1150
#[cfg(feature = "python_binding")]
1151
pub unsafe extern "C" fn qk_circuit_to_python(
180✔
1152
    circuit: *mut CircuitData,
180✔
1153
) -> *mut ::pyo3::ffi::PyObject {
180✔
1154
    // SAFETY: per documentation, `circuit` is a valid and owned `CircuitData`.
1155
    let circuit = unsafe { Box::from_raw(mut_ptr_as_ref(circuit)) };
180✔
1156
    // SAFETY: per documentation, we are attached to an interpreter.
1157
    let py = unsafe { ::pyo3::Python::assume_attached() };
180✔
1158
    circuit
180✔
1159
        .into_py_quantum_circuit(py)
180✔
1160
        .expect("Unable to create a Python circuit")
180✔
1161
        .into_ptr()
180✔
1162
}
180✔
1163

1164
/// @ingroup QkCircuit
1165
///
1166
/// Units for circuit delays.
1167
#[repr(u8)]
1168
pub enum CDelayUnit {
1169
    /// Seconds.
1170
    S = 0,
1171
    /// Milliseconds.
1172
    MS = 1,
1173
    /// Microseconds.
1174
    US = 2,
1175
    /// Nanoseconds.
1176
    NS = 3,
1177
    /// Picoseconds.
1178
    PS = 4,
1179
}
1180

1181
impl From<CDelayUnit> for DelayUnit {
1182
    fn from(value: CDelayUnit) -> Self {
4✔
1183
        match value {
4✔
1184
            CDelayUnit::S => DelayUnit::S,
4✔
1185
            CDelayUnit::MS => DelayUnit::MS,
×
1186
            CDelayUnit::US => DelayUnit::US,
×
1187
            CDelayUnit::NS => DelayUnit::NS,
×
1188
            CDelayUnit::PS => DelayUnit::PS,
×
1189
        }
1190
    }
4✔
1191
}
1192

1193
/// @ingroup QkCircuit
1194
/// Append a delay instruction to the circuit.
1195
///
1196
/// @param circuit A pointer to the circuit to add the delay to.
1197
/// @param qubit The ``uint32_t`` index of the qubit to apply the delay to.
1198
/// @param duration The duration of the delay.
1199
/// @param unit An enum representing the unit of the duration.
1200
///
1201
/// @return An exit code.
1202
///
1203
/// # Example
1204
/// ```c
1205
///     QkCircuit *qc = qk_circuit_new(1, 0);
1206
///     qk_circuit_delay(qc, 0, 100.0, QkDelayUnit_NS);
1207
/// ```
1208
///
1209
/// # Safety
1210
///
1211
/// Behavior is undefined if ``circuit`` is not a valid, non-null pointer to a ``QkCircuit``.
1212
#[unsafe(no_mangle)]
1213
pub unsafe extern "C" fn qk_circuit_delay(
4✔
1214
    circuit: *mut CircuitData,
4✔
1215
    qubit: u32,
4✔
1216
    duration: f64,
4✔
1217
    unit: CDelayUnit,
4✔
1218
) -> ExitCode {
4✔
1219
    // SAFETY: Per documentation, the pointer is non-null and aligned.
1220
    let circuit = unsafe { mut_ptr_as_ref(circuit) };
4✔
1221

1222
    let delay_unit_variant = unit.into();
4✔
1223

1224
    let duration_param: Param = duration.into();
4✔
1225
    let delay_instruction = StandardInstruction::Delay(delay_unit_variant);
4✔
1226

1227
    let params = Parameters::Params(smallvec![duration_param]);
4✔
1228
    circuit
4✔
1229
        .push_packed_operation(
4✔
1230
            PackedOperation::from_standard_instruction(delay_instruction),
4✔
1231
            Some(params),
4✔
1232
            &[Qubit(qubit)],
4✔
1233
            &[],
4✔
1234
        )
1235
        .unwrap();
4✔
1236

1237
    ExitCode::Success
4✔
1238
}
4✔
1239

1240
/// @ingroup QkCircuit
1241
/// Convert a given circuit to a DAG.
1242
///
1243
/// The new DAG is copied from the circuit; the original ``circuit`` reference is still owned by the
1244
/// caller and still required to be freed with `qk_circuit_free`.  You must free the returned DAG
1245
/// with ``qk_dag_free`` when done with it.
1246
///
1247
/// @param circuit A pointer to the circuit from which to create the DAG.
1248
///
1249
/// @return A pointer to the new DAG.
1250
///
1251
/// # Example
1252
/// ```c
1253
///     QkCircuit *qc = qk_circuit_new(0, 0);
1254
///     QkQuantumRegister *qr = qk_quantum_register_new(3, "qr");
1255
///     qk_circuit_add_quantum_register(qc, qr);
1256
///     qk_quantum_register_free(qr);
1257
///
1258
///     QkDag *dag = qk_circuit_to_dag(qc);
1259
///
1260
///     qk_dag_free(dag);
1261
///     qk_circuit_free(qc);
1262
/// ```
1263
///
1264
/// # Safety
1265
///
1266
/// Behavior is undefined if ``circuit`` is not a valid, non-null pointer to a ``QkCircuit``.
1267
#[unsafe(no_mangle)]
1268
pub unsafe extern "C" fn qk_circuit_to_dag(circuit: *const CircuitData) -> *mut DAGCircuit {
4✔
1269
    // SAFETY: Per documentation, the pointer is non-null and aligned.
1270
    let circuit = unsafe { const_ptr_as_ref(circuit) };
4✔
1271

1272
    let dag = DAGCircuit::from_circuit_data(circuit, true, None, None, None, None)
4✔
1273
        .expect("Error occurred while converting CircuitData to DAGCircuit");
4✔
1274

1275
    Box::into_raw(Box::new(dag))
4✔
1276
}
4✔
1277

1278
/// @ingroup QkCircuit
1279
///
1280
/// The mode to copy the classical variables, for operations that create a new
1281
/// circuit based on an existing one.
1282
#[repr(u8)]
1283
pub enum CVarsMode {
1284
    /// Each variable has the same type it had in the input.
1285
    Alike = 0,
1286
    /// Each variable becomes a "capture".
1287
    Captures = 1,
1288
    /// Do not copy the variable data.
1289
    Drop = 2,
1290
}
1291

1292
impl From<CVarsMode> for VarsMode {
1293
    fn from(value: CVarsMode) -> Self {
8✔
1294
        match value {
8✔
1295
            CVarsMode::Alike => VarsMode::Alike,
8✔
1296
            CVarsMode::Captures => VarsMode::Captures,
×
1297
            CVarsMode::Drop => VarsMode::Drop,
×
1298
        }
1299
    }
8✔
1300
}
1301

1302
/// @ingroup QkCircuit
1303
///
1304
/// The mode to use to copy blocks in control-flow instructions, for operations that
1305
/// create a new circuit based on an existing one.
1306
#[repr(u8)]
1307
pub enum CBlocksMode {
1308
    /// Drop the blocks.
1309
    Drop = 0,
1310
    /// Keep the blocks.
1311
    Keep = 1,
1312
}
1313

1314
impl From<CBlocksMode> for BlocksMode {
1315
    fn from(value: CBlocksMode) -> Self {
8✔
1316
        match value {
8✔
1317
            CBlocksMode::Drop => BlocksMode::Drop,
8✔
1318
            CBlocksMode::Keep => BlocksMode::Keep,
×
1319
        }
1320
    }
8✔
1321
}
1322

1323
/// @ingroup QkCircuit
1324
/// Return a copy of self with the same structure but empty.
1325
///
1326
/// That structure includes:
1327
/// * global phase
1328
/// * all the qubits and clbits, including the registers.
1329
///
1330
/// @param circuit A pointer to the circuit to copy.
1331
/// @param vars_mode The mode for handling classical variables.
1332
/// @param blocks_mode The mode for handling blocks.
1333
///
1334
/// @return The pointer to the copied circuit.
1335
///
1336
/// # Example
1337
/// ```c
1338
/// QkCircuit *qc = qk_circuit_new(10, 10);
1339
/// for (int i = 0; i < 10; i++) {
1340
///     qk_circuit_measure(qc, i, i);
1341
///     uint32_t qubits[1] = {i};
1342
///     qk_circuit_gate(qc, QkGate_H, qubits, NULL);
1343
/// }
1344
///
1345
/// // As the circuit does not contain any control-flow instructions,
1346
/// // vars_mode and blocks_mode do not have any effect.
1347
/// QkCircuit *copy = qk_circuit_copy_empty_like(qc, QkVarsMode_Alike, QkBlocksMode_Drop);
1348
///
1349
/// size_t num_copy_instructions = qk_circuit_num_instructions(copy); // 0
1350
///
1351
/// // do something with the copy
1352
///
1353
/// qk_circuit_free(qc);
1354
/// qk_circuit_free(copy);
1355
/// ```
1356
///
1357
/// # Safety
1358
///
1359
/// Behavior is undefined if ``circuit`` is not a valid, non-null pointer to a ``QkCircuit``.
1360
#[unsafe(no_mangle)]
1361
pub unsafe extern "C" fn qk_circuit_copy_empty_like(
4✔
1362
    circuit: *const CircuitData,
4✔
1363
    vars_mode: CVarsMode,
4✔
1364
    blocks_mode: CBlocksMode,
4✔
1365
) -> *mut CircuitData {
4✔
1366
    // SAFETY: Per documentation, the pointer is to valid data.
1367
    let circuit = unsafe { const_ptr_as_ref(circuit) };
4✔
1368
    let vars_mode = vars_mode.into();
4✔
1369
    let blocks_mode = blocks_mode.into();
4✔
1370

1371
    let copied_circuit = circuit
4✔
1372
        .copy_empty_like(vars_mode, blocks_mode)
4✔
1373
        .expect("Failed to copy the circuit.");
4✔
1374
    Box::into_raw(Box::new(copied_circuit))
4✔
1375
}
4✔
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

© 2026 Coveralls, Inc