Skip to content

Commit e868803

Browse files
raynelfssmtreinishCryoris
authored
C API: Burn Optimize1qGatesDecomposition. (Qiskit#14710)
* Initial: Introduce C-API Access to `Optimize1qGatesDecomposition`. - The following commit adds initial access to a standalone C foreign function interface that allows users to run the `Optimize1qGatesDecomposition` transpiler pass. The feature as it stands requires the user to build a target prior to submitting the circuit; This might change in the future. - Added 1 initial testing suite for the pass which converts a sequence of H gates into a different sequences depending on the provided target. More tests will be added with time. * Fix: Add target identity test. * Chore: Fix docstring and free some pointers. * Add: Support for optional target for `Optimize1qGatesDecomposition` C API. - Add ability of running the pass without passing a `Target`. - Add proper formatting to unit tests with multiple tests inside. - Add tests without target. * Chore: Add release note. * Chore: Fix docstring. * Chore: Fix bug in release note * Chore: Address review comments - Make tha function call run in-place and modify the pointer to the `QkCircuit` in use. - Remove `h_p_target` from tests. - Use parametric gates in the target with generic parameter expressions instead of fixed ones. * Update crates/cext/src/transpiler/passes/optimize_1q_decomposition.rs Co-authored-by: Matthew Treinish <[email protected]> * Chore: Fix docstring * Chore: Fix release note * Update test/c/test_optimize_1q_decomposition.c Co-authored-by: Julien Gacon <[email protected]> * Chore: Address review comments. * Fix: Review comments * Fix: Address some memory leakage. * Fix: Incorrect function name call * Chore: Address different review items - Rename files from `optimize_1q_decomposition` to `optiize_1q_sequences`. - Fixed previous oversight that counted operations before the pass was ran. * Fix: Replace `QkOpCounts` failures until it is fixed. * Fix: Add final review comments. - Update release note to include explanation as to what happens if a `Target` is not provided. - Update helper function `compare_gate_counts` to take `counts` by reference. * Fix: Cformatting issues. * Fix: More c formatting issues. --------- Co-authored-by: Matthew Treinish <[email protected]> Co-authored-by: Julien Gacon <[email protected]>
1 parent ce16e1a commit e868803

File tree

4 files changed

+438
-0
lines changed

4 files changed

+438
-0
lines changed

crates/cext/src/transpiler/passes/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
pub mod elide_permutations;
1414
pub mod gate_direction;
1515
pub mod inverse_cancellation;
16+
pub mod optimize_1q_sequences;
1617
pub mod remove_diagonal_gates_before_measure;
1718
pub mod remove_identity_equiv;
1819
pub mod sabre_layout;
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
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 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 crate::pointers::{const_ptr_as_ref, mut_ptr_as_ref};
14+
use qiskit_circuit::{
15+
circuit_data::CircuitData, converters::dag_to_circuit, dag_circuit::DAGCircuit,
16+
};
17+
use qiskit_transpiler::{passes::run_optimize_1q_gates_decomposition, target::Target};
18+
19+
/// @ingroup QkTranspilerPasses
20+
/// Runs the Optimize1qGatesDecomposition pass in standalone mode on a circuit.
21+
///
22+
/// Optimize1qGatesDecomposition optimizes single-qubit gate sequences by re-synthesizing
23+
/// the unitary under the constraints of the target's basis gates and error rates.
24+
///
25+
/// The decision of whether to replace the original chain depends on:
26+
/// - If the original chain was out of basis.
27+
/// - If the original chain was in basis but the replacement has lower error rates.
28+
/// - If the original chain is an identity (chain gets removed).
29+
///
30+
/// The error is the combined multiplication of the errors of individual gates on the
31+
/// qubit it operates on.
32+
///
33+
/// @param circuit A pointer to the ``QkCircuit`` object to transform.
34+
/// @param target A pointer to the ``QkTarget`` object or a null pointer.
35+
/// In the case a null pointer is provided and gate errors are unknown
36+
/// the pass will choose the sequence with the least amount of gates,
37+
/// and will support all basis gates on its Euler basis set.
38+
///
39+
/// # Example
40+
///
41+
/// QkTarget *target = qk_target_new(1);
42+
/// double u_errors[3] = {0., 1e-4, 1e-4};
43+
/// for (int idx = 0; idx < 3; idx++) {
44+
/// QkTargetEntry *u_entry = qk_target_entry_new(QkGate_U);
45+
/// uint32_t qargs[1] = {
46+
/// 0,
47+
/// };
48+
/// qk_target_entry_add_property(u_entry, qargs, 1, NAN, u_errors[idx]);
49+
/// qk_target_add_instruction(target, u_entry);
50+
/// }
51+
///
52+
/// // Build circuit
53+
/// QkCircuit *circuit = qk_circuit_new(1, 0);
54+
/// uint32_t qubits[1] = {0};
55+
/// for (int iter = 0; iter < 3; iter++) {
56+
/// qk_circuit_gate(circuit, QkGate_H, qubits, NULL);
57+
/// }
58+
///
59+
/// // Run transpiler pass
60+
/// qk_transpiler_standalone_optimize_1q_sequences(circuit, target);
61+
///
62+
/// // Clean up
63+
/// qk_target_free(target);
64+
/// qk_circuit_free(circuit);
65+
///
66+
/// # Safety
67+
///
68+
/// Behavior is undefined if ``circuit`` is not a valid, non-null pointer to a ``QkCircuit`` and
69+
/// if ``target`` is not a valid pointer to a ``QkTarget``.
70+
#[no_mangle]
71+
#[cfg(feature = "cbinding")]
72+
pub unsafe extern "C" fn qk_transpiler_standalone_optimize_1q_sequences(
73+
circuit: *mut CircuitData,
74+
target: *const Target,
75+
) {
76+
// SAFETY: Per documentation, the pointer is non-null and aligned.
77+
let target = unsafe {
78+
if target.is_null() {
79+
None
80+
} else {
81+
Some(const_ptr_as_ref(target))
82+
}
83+
};
84+
// SAFETY: Per documentation, the pointer is non-null and aligned.
85+
let circuit = unsafe { mut_ptr_as_ref(circuit) };
86+
87+
// Convert the circuit to a DAG.
88+
let mut circuit_as_dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None)
89+
.expect("Error while converting the circuit to a dag.");
90+
91+
// Run the pass
92+
run_optimize_1q_gates_decomposition(&mut circuit_as_dag, target, None, None)
93+
.expect("Error while running the pass.");
94+
95+
// Convert the DAGCircuit back to an instance of CircuitData
96+
let dag_to_circuit = dag_to_circuit(&circuit_as_dag, false)
97+
.expect("Error while converting the dag to a circuit.");
98+
99+
// Convert to pointer.
100+
*circuit = dag_to_circuit;
101+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
features_c:
3+
- |
4+
Add support for the :class:`.Optimize1qGatesDecomposition` transpiler pass through
5+
the C API, via the :cpp:func:`qk_transpiler_standalone_optimize_1q_sequences`
6+
method.
7+
8+
Here's an example:
9+
10+
.. code-block:: C
11+
12+
#include <qiskit.h>
13+
14+
// Build a circuit
15+
QkCircuit *circuit = qk_circuit_new(1, 0);
16+
uint32_t qubits[1] = {0};
17+
double params_pos[1] = {3.14 / 7.};
18+
double params_neg[1] = {-3.14 / 7.};
19+
qk_circuit_gate(circuit, QkGate_RY, qubits, params_pos);
20+
qk_circuit_gate(circuit, QkGate_RY, qubits, params_neg);
21+
22+
// Run the transpiler pass with no Target, which signals the pass to choose
23+
// the sequence wiith the least amount of gates and support all basis gates
24+
// on its Euler basis set.
25+
qk_transpiler_standalone_optimize_1q_sequences(circuit, NULL);
26+
27+
// Free both circuits
28+
qk_circuit_free(circuit);

0 commit comments

Comments
 (0)